diff --git a/src/tools/miri/.gitignore b/src/tools/miri/.gitignore index 924a93e807fe3..97e006e8b1b82 100644 --- a/src/tools/miri/.gitignore +++ b/src/tools/miri/.gitignore @@ -9,5 +9,5 @@ tex/*/out perf.data perf.data.old flamegraph.svg -tests/extern-so/libtestlib.so +tests/native-lib/libtestlib.so .auto-* diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 60bc1d5282dd6..092ad46a7cad3 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -72,14 +72,14 @@ For example: You can (cross-)run the entire test suite using: -``` +```sh ./miri test -MIRI_TEST_TARGET=i686-unknown-linux-gnu ./miri test +./miri test --target i686-unknown-linux-gnu ``` `./miri test FILTER` only runs those tests that contain `FILTER` in their filename (including the -base directory, e.g. `./miri test fail` will run all compile-fail tests). These filters are passed -to `cargo test`, so for multiple filters you need to use `./miri test -- FILTER1 FILTER2`. +base directory, e.g. `./miri test fail` will run all compile-fail tests). Multiple filters are +supported: `./miri test FILTER1 FILTER2` runs all tests that contain either string. #### Fine grained logging @@ -139,9 +139,8 @@ and then you can use it as if it was installed by `rustup` as a component of the in the `miri` toolchain's sysroot to prevent conflicts with other toolchains. The Miri binaries in the `cargo` bin directory (usually `~/.cargo/bin`) are managed by rustup. -There's a test for the cargo wrapper in the `test-cargo-miri` directory; run -`./run-test.py` in there to execute it. Like `./miri test`, this respects the -`MIRI_TEST_TARGET` environment variable to execute the test for another target. +There's a test for the cargo wrapper in the `test-cargo-miri` directory; run `./run-test.py` in +there to execute it. You can pass `--target` to execute the test for another target. ### Using a modified standard library @@ -287,3 +286,41 @@ https. Add the following to your `.gitconfig`: [url "git@github.com:"] pushInsteadOf = https://github.com/ ``` + +## Internal environment variables + +The following environment variables are *internal* and must not be used by +anyone but Miri itself. They are used to communicate between different Miri +binaries, and as such worth documenting: + +* `CARGO_EXTRA_FLAGS` is understood by `./miri` and passed to all host cargo invocations. +* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to + actually not interpret the code but compile it like rustc would. With `target`, Miri sets + some compiler flags to prepare the code for interpretation; with `host`, this is not done. + This environment variable is useful to be sure that the compiled `rlib`s are compatible + with Miri. +* `MIRI_CALLED_FROM_SETUP` is set during the Miri sysroot build, + which will re-invoke `cargo-miri` as the `rustc` to use for this build. +* `MIRI_CALLED_FROM_RUSTDOC` when set to any value tells `cargo-miri` that it is + running as a child process of `rustdoc`, which invokes it twice for each doc-test + and requires special treatment, most notably a check-only build before interpretation. + This is set by `cargo-miri` itself when running as a `rustdoc`-wrapper. +* `MIRI_CWD` when set to any value tells the Miri driver to change to the given + directory after loading all the source files, but before commencing + interpretation. This is useful if the interpreted program wants a different + working directory at run-time than at build-time. +* `MIRI_LOCAL_CRATES` is set by `cargo-miri` to tell the Miri driver which + crates should be given special treatment in diagnostics, in addition to the + crate currently being compiled. +* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the + value of `RUSTDOC` from before it was overwritten. +* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap + run the standard library tests in Miri. +* `MIRI_TEST_TARGET` is set by `./miri test` (and `./x.py test miri`) to tell the test harness about + the chosen target. +* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to + perform verbose logging. +* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* + operations. +* `RUSTC_BLESS` is set by `./miri test` (and `./x.py test miri`) to indicate bless-mode to the test + harness. diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 5b3e2a588b4fa..208a8b9ee617d 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -1,39 +1,34 @@ # Miri -An experimental interpreter for [Rust][rust]'s -[mid-level intermediate representation][mir] (MIR). It can run binaries and -test suites of cargo projects and detect certain classes of -[undefined behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html), -for example: +Miri is an [Undefined Behavior][reference-ub] detection tool for Rust. It can run binaries and test +suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For +instance: * Out-of-bounds memory accesses and use-after-free * Invalid use of uninitialized data * Violation of intrinsic preconditions (an [`unreachable_unchecked`] being reached, calling [`copy_nonoverlapping`] with overlapping ranges, ...) * Not sufficiently aligned memory accesses and references -* Violation of *some* basic type invariants (a `bool` that is not 0 or 1, for example, +* Violation of basic type invariants (a `bool` that is not 0 or 1, for example, or an invalid enum discriminant) * **Experimental**: Violations of the [Stacked Borrows] rules governing aliasing for reference types * **Experimental**: Violations of the [Tree Borrows] aliasing rules, as an optional alternative to [Stacked Borrows] -* **Experimental**: Data races +* **Experimental**: Data races and emulation of weak memory effects, i.e., + atomic reads can return outdated values. On top of that, Miri will also tell you about memory leaks: when there is memory still allocated at the end of the execution, and that memory is not reachable from a global `static`, Miri will raise an error. -Miri supports almost all Rust language features; in particular, unwinding and -concurrency are properly supported (including some experimental emulation of -weak memory effects, i.e., reads can return outdated values). - You can use Miri to emulate programs on other targets, e.g. to ensure that byte-level data manipulation works correctly both on little-endian and big-endian systems. See [cross-interpretation](#cross-interpretation-running-for-different-targets) below. -Miri has already discovered some [real-world bugs](#bugs-found-by-miri). If you +Miri has already discovered many [real-world bugs](#bugs-found-by-miri). If you found a bug with Miri, we'd appreciate if you tell us and we'll add it to the list! @@ -45,24 +40,27 @@ clocks, are replaced by deterministic "fake" implementations. Set (In particular, the "fake" system RNG APIs make Miri **not suited for cryptographic use**! Do not generate keys using Miri.) -All that said, be aware that Miri will **not catch all cases of undefined -behavior** in your program, and cannot run all programs: +All that said, be aware that Miri does **not catch every violation of the Rust specification** in +your program, not least because there is no such specification. Miri uses its own approximation of +what is and is not Undefined Behavior in Rust. To the best of our knowledge, all Undefined Behavior +that has the potential to affect a program's correctness *is* being detected by Miri (modulo +[bugs][I-misses-ub]), but you should consult [the Reference][reference-ub] for the official +definition of Undefined Behavior. Miri will be updated with the Rust compiler to protect against UB +as it is understood by the current compiler, but it makes no promises about future versions of +rustc. -* There are still plenty of open questions around the basic invariants for some - types and when these invariants even have to hold. Miri tries to avoid false - positives here, so if your program runs fine in Miri right now that is by no - means a guarantee that it is UB-free when these questions get answered. +Further caveats that Miri users should be aware of: - In particular, Miri does not check that references point to valid data. * If the program relies on unspecified details of how data is laid out, it will still run fine in Miri -- but might break (including causing UB) on different - compiler versions or different platforms. + compiler versions or different platforms. (You can use `-Zrandomize-layout` + to detect some of these cases.) * Program execution is non-deterministic when it depends, for example, on where exactly in memory allocations end up, or on the exact interleaving of concurrent threads. Miri tests one of many possible executions of your - program. You can alleviate this to some extent by running Miri with different - values for `-Zmiri-seed`, but that will still by far not explore all possible - executions. + program, but it will miss bugs that only occur in a different possible execution. + You can alleviate this to some extent by running Miri with different + values for `-Zmiri-seed`, but that will still by far not explore all possible executions. * Miri runs the program as a platform-independent interpreter, so the program has no access to most platform-specific APIs or FFI. A few APIs have been implemented (such as printing to stdout, accessing environment variables, and @@ -70,8 +68,8 @@ behavior** in your program, and cannot run all programs: not support networking. System API support varies between targets; if you run on Windows it is a good idea to use `--target x86_64-unknown-linux-gnu` to get better support. -* Weak memory emulation may [produce weak behaviours](https://github.com/rust-lang/miri/issues/2301) - unobservable by compiled programs running on real hardware when `SeqCst` fences are used, and it +* Weak memory emulation may [produce weak behaviors](https://github.com/rust-lang/miri/issues/2301) + when `SeqCst` fences are used that are not actually permitted by the Rust memory model, and it cannot produce all behaviors possibly observable on real hardware. Moreover, Miri fundamentally cannot tell you whether your code is *sound*. [Soundness] is the property @@ -87,6 +85,8 @@ coverage. [Stacked Borrows]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md [Tree Borrows]: https://perso.crans.org/vanille/treebor/ [Soundness]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library +[reference-ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html +[I-misses-ub]: https://github.com/rust-lang/miri/labels/I-misses-UB ## Using Miri @@ -97,14 +97,8 @@ Install Miri on Rust nightly via `rustup`: rustup +nightly component add miri ``` -If `rustup` says the `miri` component is unavailable, that's because not all -nightly releases come with all tools. Check out -[this website](https://rust-lang.github.io/rustup-components-history) to -determine a nightly version that comes with Miri and install that using `rustup -toolchain install nightly-YYYY-MM-DD`. Either way, all of the following commands -assume the right toolchain is pinned via `rustup override set nightly` or -`rustup override set nightly-YYYY-MM-DD`. (Alternatively, use `cargo -+nightly`/`cargo +nightly-YYYY-MM-DD` for each of the following commands.) +All the following commands assume the nightly toolchain is pinned via `rustup override set nightly`. +Alternatively, use `cargo +nightly` for each of the following commands. Now you can run your project in Miri: @@ -118,12 +112,12 @@ dependencies. It will ask you for confirmation before installing anything. example, `cargo miri test filter` only runs the tests containing `filter` in their name. -You can pass arguments to Miri via `MIRIFLAGS`. For example, +You can pass [flags][miri-flags] to Miri via `MIRIFLAGS`. For example, `MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri run` runs the program without checking the aliasing of references. When compiling code via `cargo miri`, the `cfg(miri)` config flag is set for code -that will be interpret under Miri. You can use this to ignore test cases that fail +that will be interpreted under Miri. You can use this to ignore test cases that fail under Miri because they do things Miri does not support: ```rust @@ -159,10 +153,8 @@ endian-sensitive code. ### Running Miri on CI -To run Miri on CI, make sure that you handle the case where the latest nightly -does not ship the Miri component because it currently does not build. `rustup -toolchain install --component` knows how to handle this situation, so the -following snippet should always work: +When running Miri on CI, use the following snippet to install a nightly toolchain with the Miri +component: ```sh rustup toolchain install nightly --component miri @@ -227,7 +219,7 @@ degree documented below): - We have unofficial support (not maintained by the Miri team itself) for some further operating systems. - `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - - `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works. + - `solaris` / `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works. - `wasm`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. @@ -273,25 +265,12 @@ To get a backtrace, you need to disable isolation RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test ``` -#### "found possibly newer version of crate `std` which `` depends on" - -Your build directory may contain artifacts from an earlier build that have/have -not been built for Miri. Run `cargo clean` before switching from non-Miri to -Miri builds and vice-versa. - #### "found crate `std` compiled by an incompatible version of rustc" You may be running `cargo miri` with a different compiler version than the one used to build the custom libstd that Miri uses, and Miri failed to detect that. Try running `cargo miri clean`. -#### "no mir for `std::rt::lang_start_internal`" - -This means the sysroot you are using was not compiled with Miri in mind. This -should never happen when you use `cargo miri` because that takes care of setting -up the sysroot. If you are using `miri` (the Miri driver) directly, see the -[contributors' guide](CONTRIBUTING.md) for how to use `./miri` to best do that. - ## Miri `-Z` flags and environment variables [miri-flags]: #miri--z-flags-and-environment-variables @@ -395,17 +374,17 @@ to Miri failing to detect cases of undefined behavior in a program. this flag is **unsound**. * `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak memory effects. -* `-Zmiri-extern-so-file=` is an experimental flag for providing support - for FFI calls. Functions not provided by that file are still executed via the usual Miri shims. - **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself! - And of course, Miri cannot do any checks on the actions taken by the external code. +* `-Zmiri-native-lib=` is an experimental flag for providing support + for calling native functions from inside the interpreter via FFI. Functions not provided by that + file are still executed via the usual Miri shims. + **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself! + And of course, Miri cannot do any checks on the actions taken by the native code. Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions working on file descriptors, you will have to replace *all* of them, or the two kinds of file descriptors will be mixed up. This is **work in progress**; currently, only integer arguments and return values are supported (and no, pointer/integer casts to work around this limitation will not work; - they will fail horribly). It also only works on unix hosts for now. - Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365). + they will fail horribly). It also only works on Linux hosts for now. * `-Zmiri-measureme=` enables `measureme` profiling for the interpreted program. This can be used to find which parts of your program are executing slowly under Miri. The profile is written out to a file inside a directory called ``, and can be processed @@ -484,50 +463,14 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the automatically created sysroot. When invoking `cargo miri setup`, this indicates where the sysroot will be put. -* `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target - architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same - purpose. * `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests. By default, the number of cores is used. * `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target name; this environment variable is only needed when that heuristic fails.) -* `RUSTC_BLESS` (recognized by `./miri test` and `cargo-miri-test/run-test.py`): overwrite all - `stderr` and `stdout` files instead of checking whether the output matches. * `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the `stderr` or `stdout` files match the actual output. -The following environment variables are *internal* and must not be used by -anyone but Miri itself. They are used to communicate between different Miri -binaries, and as such worth documenting: - -* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to - actually not interpret the code but compile it like rustc would. With `target`, Miri sets - some compiler flags to prepare the code for interpretation; with `host`, this is not done. - This environment variable is useful to be sure that the compiled `rlib`s are compatible - with Miri. -* `MIRI_CALLED_FROM_SETUP` is set during the Miri sysroot build, - which will re-invoke `cargo-miri` as the `rustc` to use for this build. -* `MIRI_CALLED_FROM_RUSTDOC` when set to any value tells `cargo-miri` that it is - running as a child process of `rustdoc`, which invokes it twice for each doc-test - and requires special treatment, most notably a check-only build before interpretation. - This is set by `cargo-miri` itself when running as a `rustdoc`-wrapper. -* `MIRI_CWD` when set to any value tells the Miri driver to change to the given - directory after loading all the source files, but before commencing - interpretation. This is useful if the interpreted program wants a different - working directory at run-time than at build-time. -* `MIRI_LOCAL_CRATES` is set by `cargo-miri` to tell the Miri driver which - crates should be given special treatment in diagnostics, in addition to the - crate currently being compiled. -* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the - value of `RUSTDOC` from before it was overwritten. -* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap - run the standard library tests in Miri. -* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to - perform verbose logging. -* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* - operations. - [testing-miri]: CONTRIBUTING.md#testing-the-miri-driver ## Miri `extern` functions diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 713c97830a5a2..f5fbb05d89622 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -31,24 +31,26 @@ time ./miri build --all-targets # the build that all the `./miri test` below wil endgroup # Run tests. Recognizes these variables: -# - MIRI_TEST_TARGET: the target to test. Empty for host target. +# - TEST_TARGET: the target to test. Empty for host target. # - GC_STRESS: if non-empty, run the GC stress test for the main test suite. # - MIR_OPT: if non-empty, re-run test `pass` tests with mir-opt-level=4 # - MANY_SEEDS: if set to N, run the "many-seeds" tests N times # - TEST_BENCH: if non-empty, check that the benchmarks all build # - CARGO_MIRI_ENV: if non-empty, set some env vars and config to potentially confuse cargo-miri function run_tests { - if [ -n "${MIRI_TEST_TARGET-}" ]; then - begingroup "Testing foreign architecture $MIRI_TEST_TARGET" + if [ -n "${TEST_TARGET-}" ]; then + begingroup "Testing foreign architecture $TEST_TARGET" + TARGET_FLAG="--target $TEST_TARGET" else begingroup "Testing host architecture" + TARGET_FLAG="" fi ## ui test suite if [ -n "${GC_STRESS-}" ]; then - time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test + time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test $TARGET_FLAG else - time ./miri test + time ./miri test $TARGET_FLAG fi ## advanced tests @@ -59,17 +61,17 @@ function run_tests { # them. Also error locations change so we don't run the failing tests. # We explicitly enable debug-assertions here, they are disabled by -O but we have tests # which exist to check that we panic on debug assertion failures. - time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic} + time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test $TARGET_FLAG tests/{pass,panic} fi if [ -n "${MANY_SEEDS-}" ]; then # Also run some many-seeds tests. time for FILE in tests/many-seeds/*.rs; do - ./miri run "--many-seeds=0..$MANY_SEEDS" "$FILE" + ./miri run "--many-seeds=0..$MANY_SEEDS" $TARGET_FLAG "$FILE" done fi if [ -n "${TEST_BENCH-}" ]; then # Check that the benchmarks build and run, but only once. - time HYPERFINE="hyperfine -w0 -r1" ./miri bench + time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG fi ## test-cargo-miri @@ -91,7 +93,7 @@ function run_tests { echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml fi # Run the actual test - time ${PYTHON} test-cargo-miri/run-test.py + time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG # Clean up unset RUSTC MIRI rm -rf .cargo @@ -100,17 +102,18 @@ function run_tests { } function run_tests_minimal { - if [ -n "${MIRI_TEST_TARGET-}" ]; then - begingroup "Testing MINIMAL foreign architecture $MIRI_TEST_TARGET: only testing $@" + if [ -n "${TEST_TARGET-}" ]; then + begingroup "Testing MINIMAL foreign architecture $TEST_TARGET: only testing $@" + TARGET_FLAG="--target $TEST_TARGET" else - echo "run_tests_minimal requires MIRI_TEST_TARGET to be set" + echo "run_tests_minimal requires TEST_TARGET to be set" exit 1 fi - time ./miri test -- "$@" + time ./miri test $TARGET_FLAG "$@" # Ensure that a small smoke test of cargo-miri works. - time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml --target ${MIRI_TEST_TARGET-$HOST_TARGET} + time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml $TARGET_FLAG endgroup } @@ -126,34 +129,33 @@ case $HOST_TARGET in # Extra tier 1 # With reduced many-seed count to avoid spending too much time on that. # (All OSes and ABIs are run with 64 seeds at least once though via the macOS runner.) - MANY_SEEDS=16 MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests - MANY_SEEDS=16 MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests - MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-apple-darwin run_tests - MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests + MANY_SEEDS=16 TEST_TARGET=i686-unknown-linux-gnu run_tests + MANY_SEEDS=16 TEST_TARGET=aarch64-unknown-linux-gnu run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-apple-darwin run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-pc-windows-gnu run_tests ;; aarch64-apple-darwin) # Host (tier 2) GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - MANY_SEEDS=64 MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MANY_SEEDS=64 MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests + MANY_SEEDS=64 TEST_TARGET=i686-pc-windows-gnu run_tests + MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests # Extra tier 2 - MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests - MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice + TEST_TARGET=arm-unknown-linux-gnueabi run_tests + TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Partially supported targets (tier 2) VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus - MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus - MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random - # TODO fix solaris stack guard - # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync - MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic - MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm - MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm - MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus + TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus + TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic + TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm + TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm + TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std # Custom target JSON file - MIRI_TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std + TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std ;; i686-pc-windows-msvc) # Host @@ -163,7 +165,7 @@ case $HOST_TARGET in # 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. - MIRI_TEST_TARGET=x86_64-unknown-linux-gnu run_tests + TEST_TARGET=x86_64-unknown-linux-gnu run_tests ;; *) echo "FATAL: unknown host target: $HOST_TARGET" diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 6d07455c0deb6..8e2b07ad8052a 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -1,5 +1,5 @@ use std::env; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::io::Write; use std::ops::Not; use std::ops::Range; @@ -23,7 +23,9 @@ const JOSH_PORT: &str = "42042"; impl MiriEnv { /// Returns the location of the sysroot. - fn build_miri_sysroot(&mut self, quiet: bool) -> Result { + /// + /// If the target is None the sysroot will be built for the host machine. + fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result { if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { // Sysroot already set, use that. return Ok(miri_sysroot.into()); @@ -35,26 +37,28 @@ impl MiriEnv { self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?; self.build(&manifest_path, &[], quiet)?; - let target = &match self.sh.var("MIRI_TEST_TARGET") { - Ok(target) => vec!["--target".into(), target], - Err(_) => vec![], - }; + let target_flag = + if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] }; + let target_flag = &target_flag; + if !quiet { - match self.sh.var("MIRI_TEST_TARGET") { - Ok(target) => eprintln!("$ (building Miri sysroot for {target})"), - Err(_) => eprintln!("$ (building Miri sysroot)"), + if let Some(target) = target { + eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy()); + } else { + eprintln!("$ (building Miri sysroot)"); } } + let output = cmd!(self.sh, "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} -- - miri setup --print-sysroot {target...}" + miri setup --print-sysroot {target_flag...}" ).read(); let Ok(output) = output else { // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error. cmd!( self.sh, "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} -- - miri setup {target...}" + miri setup {target_flag...}" ) .run() .with_context(|| "`cargo miri setup` failed")?; @@ -161,13 +165,13 @@ impl Command { Command::Install { flags } => Self::install(flags), Command::Build { flags } => Self::build(flags), Command::Check { flags } => Self::check(flags), - Command::Test { bless, flags } => Self::test(bless, flags), + Command::Test { bless, flags, target } => Self::test(bless, flags, target), Command::Run { dep, verbose, many_seeds, flags } => Self::run(dep, verbose, many_seeds, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { flags } => Self::clippy(flags), Command::Cargo { flags } => Self::cargo(flags), - Command::Bench { benches } => Self::bench(benches), + Command::Bench { target, benches } => Self::bench(target, 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), @@ -369,7 +373,7 @@ impl Command { Ok(()) } - fn bench(benches: Vec) -> Result<()> { + fn bench(target: Option, benches: Vec) -> Result<()> { // The hyperfine to use let hyperfine = env::var("HYPERFINE"); let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none"); @@ -377,8 +381,6 @@ impl Command { let Some((program_name, args)) = hyperfine.split_first() else { bail!("expected HYPERFINE environment variable to be non-empty"); }; - // Extra flags to pass to cargo. - let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default(); // Make sure we have an up-to-date Miri installed and selected the right toolchain. Self::install(vec![])?; @@ -394,6 +396,14 @@ impl Command { } else { benches.to_owned() }; + let target_flag = if let Some(target) = target { + let mut flag = OsString::from("--target="); + flag.push(target); + flag + } else { + OsString::new() + }; + let target_flag = &target_flag; // Run the requested benchmarks for bench in benches { let current_bench = path!(benches_dir / bench / "Cargo.toml"); @@ -401,7 +411,7 @@ impl Command { // That seems to make Windows CI happy. cmd!( sh, - "{program_name} {args...} 'cargo miri run '{cargo_extra_flags}' --manifest-path \"'{current_bench}'\"'" + "{program_name} {args...} 'cargo miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'" ) .run()?; } @@ -446,16 +456,26 @@ impl Command { Ok(()) } - fn test(bless: bool, flags: Vec) -> Result<()> { + fn test(bless: bool, mut flags: Vec, target: Option) -> Result<()> { let mut e = MiriEnv::new()?; + // Prepare a sysroot. - e.build_miri_sysroot(/* quiet */ false)?; + e.build_miri_sysroot(/* quiet */ false, target.as_deref())?; - // Then test, and let caller control flags. - // Only in root project as `cargo-miri` has no tests. + // Forward information to test harness. if bless { e.sh.set_var("RUSTC_BLESS", "Gesundheit"); } + if let Some(target) = target { + // Tell the harness which target to test. + e.sh.set_var("MIRI_TEST_TARGET", target); + } + + // Make sure the flags are going to the test harness, not cargo. + flags.insert(0, "--".into()); + + // Then test, and let caller control flags. + // Only in root project as `cargo-miri` has no tests. e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?; Ok(()) } @@ -467,32 +487,16 @@ impl Command { mut flags: Vec, ) -> Result<()> { let mut e = MiriEnv::new()?; - // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so - // that we set the MIRI_SYSROOT up the right way. We must make sure that - // MIRI_TEST_TARGET and `--target` are in sync. - use itertools::Itertools; - let target = flags - .iter() - .take_while(|arg| *arg != "--") - .tuple_windows() - .find(|(first, _)| *first == "--target"); - if let Some((_, target)) = target { - // Found it! - e.sh.set_var("MIRI_TEST_TARGET", target); - } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { - // Convert `MIRI_TEST_TARGET` into `--target`. - flags.push("--target".into()); - flags.push(target.into()); - } + let target = arg_flag_value(&flags, "--target"); + // Scan for "--edition", set one ourselves if that flag is not present. - let have_edition = - flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition"); + let have_edition = arg_flag_value(&flags, "--edition").is_some(); if !have_edition { flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` } // Prepare a sysroot, and add it to the flags. - let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose)?; + let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?; flags.push("--sysroot".into()); flags.push(miri_sysroot.into()); diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index f0ebbc846906e..a8626ceb45d75 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -31,7 +31,10 @@ pub enum Command { /// Build miri, set up a sysroot and then run the test suite. Test { bless: bool, - /// Flags that are passed through to `cargo test`. + /// The cross-interpretation target. + /// If none then the host is the target. + target: Option, + /// Flags that are passed through to the test harness. flags: Vec, }, /// Build miri, set up a sysroot and then run the driver with the given . @@ -58,6 +61,7 @@ pub enum Command { Cargo { flags: Vec }, /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. Bench { + target: Option, /// List of benchmarks to run. By default all benchmarks are run. benches: Vec, }, @@ -84,9 +88,9 @@ Just build miri. are passed to `cargo build`. ./miri check : Just check miri. are passed to `cargo check`. -./miri test [--bless] : -Build miri, set up a sysroot and then run the test suite. are passed -to the final `cargo test` invocation. +./miri test [--bless] [--target ] : +Build miri, set up a sysroot and then run the test suite. + are passed to the test harness. ./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] : Build miri, set up a sysroot and then run the driver with the given . @@ -110,7 +114,7 @@ install`. Sets up the rpath such that the installed binary should work in any working directory. Note that the binaries are placed in the `miri` toolchain sysroot, to prevent conflicts with other toolchains. -./miri bench : +./miri bench [--target ] : Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. can explicitly list the benchmarks to run; by default, all of them are run. @@ -147,12 +151,30 @@ fn main() -> Result<()> { Some("build") => Command::Build { flags: args.collect() }, Some("check") => Command::Check { flags: args.collect() }, Some("test") => { - let bless = args.peek().is_some_and(|a| a.to_str() == Some("--bless")); - if bless { - // Consume the flag. + let mut target = None; + let mut bless = false; + + while let Some(arg) = args.peek().and_then(|s| s.to_str()) { + match arg { + "--bless" => bless = true, + "--target" => { + // Skip "--target" + args.next().unwrap(); + // Next argument is the target triple. + let val = args.peek().ok_or_else(|| { + anyhow!("`--target` must be followed by target triple") + })?; + target = Some(val.to_owned()); + } + // Only parse the leading flags. + _ => break, + } + + // Consume the flag, look at the next one. args.next().unwrap(); } - Command::Test { bless, flags: args.collect() } + + Command::Test { bless, flags: args.collect(), target } } Some("run") => { let mut dep = false; @@ -188,7 +210,29 @@ fn main() -> Result<()> { Some("clippy") => Command::Clippy { flags: args.collect() }, Some("cargo") => Command::Cargo { flags: args.collect() }, Some("install") => Command::Install { flags: args.collect() }, - Some("bench") => Command::Bench { benches: args.collect() }, + Some("bench") => { + let mut target = None; + while let Some(arg) = args.peek().and_then(|s| s.to_str()) { + match arg { + "--target" => { + // Skip "--target" + args.next().unwrap(); + // Next argument is the target triple. + let val = args.peek().ok_or_else(|| { + anyhow!("`--target` must be followed by target triple") + })?; + target = Some(val.to_owned()); + } + // Only parse the leading flags. + _ => break, + } + + // Consume the flag, look at the next one. + args.next().unwrap(); + } + + Command::Bench { target, benches: args.collect() } + } Some("toolchain") => Command::Toolchain { flags: args.collect() }, Some("rustc-pull") => { let commit = args.next().map(|a| a.to_string_lossy().into_owned()); diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index 23b5e936edd81..2b5791f3ea53d 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -27,6 +27,30 @@ pub fn flagsplit(flags: &str) -> Vec { flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() } +pub fn arg_flag_value( + args: impl IntoIterator>, + flag: &str, +) -> Option { + let mut args = args.into_iter(); + while let Some(arg) = args.next() { + let arg = arg.as_ref(); + if arg == "--" { + return None; + } + let Some(arg) = arg.to_str() else { + // Skip non-UTF-8 arguments. + continue; + }; + if arg == flag { + // Next one is the value. + return Some(args.next()?.as_ref().to_owned()); + } else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) { + return Some(val.to_owned().into()); + } + } + None +} + /// Some extra state we track for building Miri, such as the right RUSTFLAGS. pub struct MiriEnv { /// miri_dir is the root of the miri repository checkout we are working in. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index ca6f4d50917e9..3636c856d0b55 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -d568423a7a4ddb4b49323d96078a22f94df55fbd +ef15976387ad9c1cdceaabf469e0cf35f5852f6d diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 44201cb89ae79..305e9cd8d3428 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -575,18 +575,15 @@ fn main() { "full" => BacktraceStyle::Full, _ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"), }; - } else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") { + } else if let Some(param) = arg.strip_prefix("-Zmiri-native-lib=") { let filename = param.to_string(); if std::path::Path::new(&filename).exists() { - if let Some(other_filename) = miri_config.external_so_file { - show_error!( - "-Zmiri-extern-so-file is already set to {}", - other_filename.display() - ); + if let Some(other_filename) = miri_config.native_lib { + show_error!("-Zmiri-native-lib is already set to {}", other_filename.display()); } - miri_config.external_so_file = Some(filename.into()); + miri_config.native_lib = Some(filename.into()); } else { - show_error!("-Zmiri-extern-so-file `{}` does not exist", filename); + show_error!("-Zmiri-native-lib `{}` does not exist", filename); } } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") { let num_cpus = param diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 97c5e9a0eac97..78c092faf9097 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -142,8 +142,8 @@ pub struct MiriConfig { /// Whether Stacked Borrows and Tree Borrows retagging should recurse into fields of datatypes. pub retag_fields: RetagFields, /// The location of a shared object file to load when calling external functions - /// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory - pub external_so_file: Option, + /// FIXME! consider allowing users to specify paths to multiple files, or to a directory + pub native_lib: Option, /// Run a garbage collector for BorTags every N basic blocks. pub gc_interval: u32, /// The number of CPUs to be reported by miri. @@ -188,7 +188,7 @@ impl Default for MiriConfig { preemption_rate: 0.01, // 1% report_progress: None, retag_fields: RetagFields::Yes, - external_so_file: None, + native_lib: None, gc_interval: 10_000, num_cpus: 1, page_size: None, diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 40c2008ac9435..4050ae3c4bf97 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -751,26 +751,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` /// as a platform-specific errnum. - fn io_error_to_errnum( - &self, - err_kind: std::io::ErrorKind, - ) -> InterpResult<'tcx, Scalar> { + fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { for &(name, kind) in UNIX_IO_ERROR_TABLE { - if err_kind == kind { + if err.kind() == kind { return Ok(this.eval_libc(name)); } } - throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind) + throw_unsup_format!("unsupported io error: {err}") } else if target.families.iter().any(|f| f == "windows") { for &(name, kind) in WINDOWS_IO_ERROR_TABLE { - if err_kind == kind { + if err.kind() == kind { return Ok(this.eval_windows("c", name)); } } - throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind); + throw_unsup_format!("unsupported io error: {err}"); } else { throw_unsup_format!( "converting io::Error into errnum is unsupported for OS {}", @@ -812,8 +809,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Sets the last OS error using a `std::io::ErrorKind`. - fn set_last_error_from_io_error(&mut self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err_kind)?) + fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { + self.set_last_error(self.io_error_to_errnum(err)?) } /// Helper function that consumes an `std::io::Result` and returns an @@ -829,7 +826,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match result { Ok(ok) => Ok(ok), Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e.kind())?; + self.eval_context_mut().set_last_error_from_io_error(e)?; Ok((-1).into()) } } @@ -982,29 +979,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((true, string_length)) } - /// Read a sequence of u16 until the first null terminator. - fn read_wide_str(&self, mut ptr: Pointer>) -> InterpResult<'tcx, Vec> { + /// Helper function to read a sequence of unsigned integers of the given size and alignment + /// until the first null terminator. + fn read_c_str_with_char_size( + &self, + mut ptr: Pointer>, + size: Size, + align: Align, + ) -> InterpResult<'tcx, Vec> + where + T: TryFrom, + >::Error: std::fmt::Debug, + { + assert_ne!(size, Size::ZERO); + let this = self.eval_context_ref(); - let size2 = Size::from_bytes(2); - this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?; + + this.check_ptr_align(ptr, align)?; let mut wchars = Vec::new(); loop { // FIXME: We are re-getting the allocation each time around the loop. // Would be nice if we could somehow "extend" an existing AllocRange. - let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result - let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?; - if wchar == 0 { + let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); // not a ZST, so we will get a result + let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?; + if wchar_int == 0 { break; } else { - wchars.push(wchar); - ptr = ptr.offset(size2, this)?; + wchars.push(wchar_int.try_into().unwrap()); + ptr = ptr.offset(size, this)?; } } Ok(wchars) } + /// Read a sequence of u16 until the first null terminator. + fn read_wide_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap()) + } + /// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying /// to write if `size` is not large enough to fit the contents of `os_string` plus a null @@ -1037,6 +1051,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((true, string_length)) } + /// Read a sequence of wchar_t until the first null terminator. + /// Always returns a `Vec` no matter the size of `wchar_t`. + fn read_wchar_t_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + let this = self.eval_context_ref(); + let wchar_t = this.libc_ty_layout("wchar_t"); + self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) + } + /// Check that the ABI is what we expect. fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { if abi != exp_abi { diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 1680b98eca33f..54eb6a3bd6699 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -12,6 +12,7 @@ #![feature(let_chains)] #![feature(lint_reasons)] #![feature(trait_upcasting)] +#![feature(strict_overflow_ops)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 51b96bff5fefc..8854b18528034 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -535,11 +535,11 @@ pub struct MiriMachine<'mir, 'tcx> { // The total number of blocks that have been executed. pub(crate) basic_block_count: u64, - /// Handle of the optional shared object file for external functions. + /// Handle of the optional shared object file for native functions. #[cfg(target_os = "linux")] - pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>, + pub native_lib: Option<(libloading::Library, std::path::PathBuf)>, #[cfg(not(target_os = "linux"))] - pub external_so_lib: Option, + pub native_lib: Option, /// Run a garbage collector for BorTags every N basic blocks. pub(crate) gc_interval: u32, @@ -665,7 +665,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { basic_block_count: 0, clock: Clock::new(config.isolated_op == IsolatedOp::Allow), #[cfg(target_os = "linux")] - external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| { + native_lib: config.native_lib.as_ref().map(|lib_file_path| { let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); // Check if host target == the session target. if env!("TARGET") != target_triple { @@ -687,7 +687,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ) }), #[cfg(not(target_os = "linux"))] - external_so_lib: config.external_so_file.as_ref().map(|_| { + native_lib: config.native_lib.as_ref().map(|_| { panic!("loading external .so files is only supported on Linux") }), gc_interval: config.gc_interval, @@ -802,7 +802,7 @@ impl VisitProvenance for MiriMachine<'_, '_> { preemption_rate: _, report_progress: _, basic_block_count: _, - external_so_lib: _, + native_lib: _, gc_interval: _, since_gc: _, num_cpus: _, diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 79531598c003a..1deb9a5654edf 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -19,22 +19,36 @@ pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<' impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Returns the minimum alignment for the target architecture for allocations of the given size. - fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { + /// Returns the alignment that `malloc` would guarantee for requests of the given size. + fn malloc_align(&self, size: u64) -> Align { let this = self.eval_context_ref(); - // List taken from `library/std/src/sys/pal/common/alloc.rs`. - // This list should be kept in sync with the one from libstd. - let min_align = match this.tcx.sess.target.arch.as_ref() { + // The C standard says: "The pointer returned if the allocation succeeds is suitably aligned + // so that it may be assigned to a pointer to any type of object with a fundamental + // alignment requirement and size less than or equal to the size requested." + // So first we need to figure out what the limits are for "fundamental alignment". + // This is given by `alignof(max_align_t)`. The following list is taken from + // `library/std/src/sys/pal/common/alloc.rs` (where this is called `MIN_ALIGN`) and should + // be kept in sync. + let max_fundamental_align = match this.tcx.sess.target.arch.as_ref() { "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => 16, arch => bug!("unsupported target architecture for malloc: `{}`", arch), }; - // Windows always aligns, even small allocations. - // Source: - // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big. - if kind == MiriMemoryKind::WinHeap || size >= min_align { - return Align::from_bytes(min_align).unwrap(); + // The C standard only requires sufficient alignment for any *type* with size less than or + // equal to the size requested. Types one can define in standard C seem to never have an alignment + // bigger than their size. So if the size is 2, then only alignment 2 is guaranteed, even if + // `max_fundamental_align` is bigger. + // This matches what some real-world implementations do, see e.g. + // - https://github.com/jemalloc/jemalloc/issues/1533 + // - https://github.com/llvm/llvm-project/issues/53540 + // - https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm + if size >= max_fundamental_align { + return Align::from_bytes(max_fundamental_align).unwrap(); + } + // C doesn't have zero-sized types, so presumably nothing is guaranteed here. + if size == 0 { + return Align::ONE; } // We have `size < min_align`. Round `size` *down* to the next power of two and use that. fn prev_power_of_two(x: u64) -> u64 { @@ -82,34 +96,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, size: u64, zero_init: bool, - kind: MiriMemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); - if size == 0 { - Ok(Pointer::null()) - } else { - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } - Ok(ptr.into()) + let align = this.malloc_align(size); + let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?; + if zero_init { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + ) + .unwrap(); } + Ok(ptr.into()) } - fn free( - &mut self, - ptr: Pointer>, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx> { + fn free(&mut self, ptr: Pointer>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); if !this.ptr_is_null(ptr)? { - this.deallocate_ptr(ptr, None, kind.into())?; + this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?; } Ok(()) } @@ -118,19 +123,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, old_ptr: Pointer>, new_size: u64, - kind: MiriMemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); - let new_align = this.min_align(new_size, kind); + let new_align = this.malloc_align(new_size); if this.ptr_is_null(old_ptr)? { // Here we must behave like `malloc`. - if new_size == 0 { - Ok(Pointer::null()) - } else { - let new_ptr = - this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?; - Ok(new_ptr.into()) - } + self.malloc(new_size, /*zero_init*/ false) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. @@ -142,7 +140,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None, Size::from_bytes(new_size), new_align, - kind.into(), + MiriMemoryKind::C.into(), )?; Ok(new_ptr.into()) } diff --git a/src/tools/miri/src/shims/ffi_support.rs b/src/tools/miri/src/shims/ffi_support.rs deleted file mode 100644 index 6da119e5fa8fb..0000000000000 --- a/src/tools/miri/src/shims/ffi_support.rs +++ /dev/null @@ -1,289 +0,0 @@ -use libffi::{high::call as ffi, low::CodePtr}; -use std::ops::Deref; - -use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy}; -use rustc_span::Symbol; -use rustc_target::abi::HasDataLayout; - -use crate::*; - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} - -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Extract the scalar value from the result of reading a scalar from the machine, - /// and convert it to a `CArg`. - fn scalar_to_carg( - k: Scalar, - arg_type: Ty<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, CArg> { - match arg_type.kind() { - // If the primitive provided can be converted to a type matching the type pattern - // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. - // the ints - ty::Int(IntTy::I8) => { - return Ok(CArg::Int8(k.to_i8()?)); - } - ty::Int(IntTy::I16) => { - return Ok(CArg::Int16(k.to_i16()?)); - } - ty::Int(IntTy::I32) => { - return Ok(CArg::Int32(k.to_i32()?)); - } - ty::Int(IntTy::I64) => { - return Ok(CArg::Int64(k.to_i64()?)); - } - ty::Int(IntTy::Isize) => { - // This will fail if host != target, but then the entire FFI thing probably won't work well - // in that situation. - return Ok(CArg::ISize(k.to_target_isize(cx)?.try_into().unwrap())); - } - // the uints - ty::Uint(UintTy::U8) => { - return Ok(CArg::UInt8(k.to_u8()?)); - } - ty::Uint(UintTy::U16) => { - return Ok(CArg::UInt16(k.to_u16()?)); - } - ty::Uint(UintTy::U32) => { - return Ok(CArg::UInt32(k.to_u32()?)); - } - ty::Uint(UintTy::U64) => { - return Ok(CArg::UInt64(k.to_u64()?)); - } - ty::Uint(UintTy::Usize) => { - // This will fail if host != target, but then the entire FFI thing probably won't work well - // in that situation. - return Ok(CArg::USize(k.to_target_usize(cx)?.try_into().unwrap())); - } - _ => {} - } - // If no primitives were returned then we have an unsupported type. - throw_unsup_format!( - "unsupported scalar argument type to external C function: {:?}", - arg_type - ); - } - - /// Call external C function and - /// store output, depending on return type in the function signature. - fn call_external_c_and_store_return<'a>( - &mut self, - link_name: Symbol, - dest: &MPlaceTy<'tcx, Provenance>, - ptr: CodePtr, - libffi_args: Vec>, - ) -> InterpResult<'tcx, ()> { - let this = self.eval_context_mut(); - - // Unsafe because of the call to external C code. - // Because this is calling a C function it is not necessarily sound, - // but there is no way around this and we've checked as much as we can. - unsafe { - // If the return type of a function is a primitive integer type, - // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified - // primitive integer type, and then write this value out to the miri memory as an integer. - match dest.layout.ty.kind() { - // ints - ty::Int(IntTy::I8) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::I16) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::I32) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::I64) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::Isize) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - // `isize` doesn't `impl Into`, so convert manually. - // Convert to `i64` since this covers both 32- and 64-bit machines. - this.write_int(i64::try_from(x).unwrap(), dest)?; - return Ok(()); - } - // uints - ty::Uint(UintTy::U8) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::U16) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::U32) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::U64) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::Usize) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - // `usize` doesn't `impl Into`, so convert manually. - // Convert to `u64` since this covers both 32- and 64-bit machines. - this.write_int(u64::try_from(x).unwrap(), dest)?; - return Ok(()); - } - // Functions with no declared return type (i.e., the default return) - // have the output_type `Tuple([])`. - ty::Tuple(t_list) => - if t_list.len() == 0 { - ffi::call::<()>(ptr, libffi_args.as_slice()); - return Ok(()); - }, - _ => {} - } - // FIXME ellen! deal with all the other return types - throw_unsup_format!("unsupported return type to external C function: {:?}", link_name); - } - } - - /// Get the pointer to the function of the specified name in the shared object file, - /// if it exists. The function must be in the shared object file specified: we do *not* - /// return pointers to functions in dependencies of the library. - fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { - let this = self.eval_context_mut(); - // Try getting the function from the shared library. - // On windows `_lib_path` will be unused, hence the name starting with `_`. - let (lib, _lib_path) = this.machine.external_so_lib.as_ref().unwrap(); - let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { - match lib.get(link_name.as_str().as_bytes()) { - Ok(x) => x, - Err(_) => { - return None; - } - } - }; - - // FIXME: this is a hack! - // The `libloading` crate will automatically load system libraries like `libc`. - // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 - // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the - // library if it can't find the symbol in the library itself. - // So, in order to check if the function was actually found in the specified - // `machine.external_so_lib` we need to check its `dli_fname` and compare it to - // the specified SO file path. - // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, - // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 - // using the `libc` crate where this interface is public. - // No `libc::dladdr` on windows. - let mut info = std::mem::MaybeUninit::::uninit(); - unsafe { - if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { - if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() - != _lib_path.to_str().unwrap() - { - return None; - } - } - } - // Return a pointer to the function. - Some(CodePtr(*func.deref() as *mut _)) - } - - /// Call specified external C function, with supplied arguments. - /// Need to convert all the arguments from their hir representations to - /// a form compatible with C (through `libffi` call). - /// Then, convert return from the C call into a corresponding form that - /// can be stored in Miri internal memory. - fn call_external_c_fct( - &mut self, - link_name: Symbol, - dest: &MPlaceTy<'tcx, Provenance>, - args: &[OpTy<'tcx, Provenance>], - ) -> InterpResult<'tcx, bool> { - // Get the pointer to the function in the shared object file if it exists. - let code_ptr = match self.get_func_ptr_explicitly_from_lib(link_name) { - Some(ptr) => ptr, - None => { - // Shared object file does not export this function -- try the shims next. - return Ok(false); - } - }; - - let this = self.eval_context_mut(); - - // Get the function arguments, and convert them to `libffi`-compatible form. - let mut libffi_args = Vec::::with_capacity(args.len()); - for cur_arg in args.iter() { - libffi_args.push(Self::scalar_to_carg( - this.read_scalar(cur_arg)?, - cur_arg.layout.ty, - this, - )?); - } - - // Convert them to `libffi::high::Arg` type. - let libffi_args = libffi_args - .iter() - .map(|cur_arg| cur_arg.arg_downcast()) - .collect::>>(); - - // Call the function and store output, depending on return type in the function signature. - self.call_external_c_and_store_return(link_name, dest, code_ptr, libffi_args)?; - Ok(true) - } -} - -#[derive(Debug, Clone)] -/// Enum of supported arguments to external C functions. -// We introduce this enum instead of just calling `ffi::arg` and storing a list -// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference -// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html -// and we need to store a copy of the value, and pass a reference to this copy to C instead. -pub enum CArg { - /// 8-bit signed integer. - Int8(i8), - /// 16-bit signed integer. - Int16(i16), - /// 32-bit signed integer. - Int32(i32), - /// 64-bit signed integer. - Int64(i64), - /// isize. - ISize(isize), - /// 8-bit unsigned integer. - UInt8(u8), - /// 16-bit unsigned integer. - UInt16(u16), - /// 32-bit unsigned integer. - UInt32(u32), - /// 64-bit unsigned integer. - UInt64(u64), - /// usize. - USize(usize), -} - -impl<'a> CArg { - /// Convert a `CArg` to a `libffi` argument type. - fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { - match self { - CArg::Int8(i) => ffi::arg(i), - CArg::Int16(i) => ffi::arg(i), - CArg::Int32(i) => ffi::arg(i), - CArg::Int64(i) => ffi::arg(i), - CArg::ISize(i) => ffi::arg(i), - CArg::UInt8(i) => ffi::arg(i), - CArg::UInt16(i) => ffi::arg(i), - CArg::UInt32(i) => ffi::arg(i), - CArg::UInt64(i) => ffi::arg(i), - CArg::USize(i) => ffi::arg(i), - } - } -} diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index c51a27b7458a8..d431c28d55a56 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -211,12 +211,12 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // First deal with any external C functions in linked .so file. #[cfg(target_os = "linux")] - if this.machine.external_so_lib.as_ref().is_some() { - use crate::shims::ffi_support::EvalContextExt as _; + if this.machine.native_lib.as_ref().is_some() { + use crate::shims::native_lib::EvalContextExt as _; // An Ok(false) here means that the function being called was not exported // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. - if this.call_external_c_fct(link_name, dest, args)? { + if this.call_native_fn(link_name, dest, args)? { return Ok(EmulateItemResult::NeedsJumping); } } @@ -421,7 +421,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "malloc" => { let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let size = this.read_target_usize(size)?; - let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?; + let res = this.malloc(size, /*zero_init:*/ false)?; this.write_pointer(res, dest)?; } "calloc" => { @@ -432,20 +432,20 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = items .checked_mul(len) .ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?; - let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?; + let res = this.malloc(size, /*zero_init:*/ true)?; this.write_pointer(res, dest)?; } "free" => { let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; - this.free(ptr, MiriMemoryKind::C)?; + this.free(ptr)?; } "realloc" => { let [old_ptr, new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let old_ptr = this.read_pointer(old_ptr)?; let new_size = this.read_target_usize(new_size)?; - let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?; + let res = this.realloc(old_ptr, new_size)?; this.write_pointer(res, dest)?; } @@ -657,6 +657,16 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest, )?; } + "wcslen" => { + let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, 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(); + this.write_scalar( + Scalar::from_target_usize(u64::try_from(n).unwrap(), this), + dest, + )?; + } "memcpy" => { let [ptr_dest, ptr_src, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 11048459206bf..aaa3c69b92da7 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -2,9 +2,9 @@ mod alloc; mod backtrace; -#[cfg(target_os = "linux")] -pub mod ffi_support; pub mod foreign_items; +#[cfg(target_os = "linux")] +pub mod native_lib; pub mod unix; pub mod windows; mod x86; diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs new file mode 100644 index 0000000000000..f9b8563b4b04e --- /dev/null +++ b/src/tools/miri/src/shims/native_lib.rs @@ -0,0 +1,242 @@ +//! Implements calling functions from a native library. +use libffi::{high::call as ffi, low::CodePtr}; +use std::ops::Deref; + +use rustc_middle::ty::{self as ty, IntTy, UintTy}; +use rustc_span::Symbol; +use rustc_target::abi::{Abi, HasDataLayout}; + +use crate::*; + +impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Call native host function and return the output as an immediate. + fn call_native_with_args<'a>( + &mut self, + link_name: Symbol, + dest: &MPlaceTy<'tcx, Provenance>, + ptr: CodePtr, + libffi_args: Vec>, + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + let this = self.eval_context_mut(); + + // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value + // as the specified primitive integer type + let scalar = match dest.layout.ty.kind() { + // ints + ty::Int(IntTy::I8) => { + // Unsafe because of the call to native code. + // Because this is calling a C function it is not necessarily sound, + // but there is no way around this and we've checked as much as we can. + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i8(x) + } + ty::Int(IntTy::I16) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i16(x) + } + ty::Int(IntTy::I32) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i32(x) + } + ty::Int(IntTy::I64) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i64(x) + } + ty::Int(IntTy::Isize) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_target_isize(x.try_into().unwrap(), this) + } + // uints + ty::Uint(UintTy::U8) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u8(x) + } + ty::Uint(UintTy::U16) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u16(x) + } + ty::Uint(UintTy::U32) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u32(x) + } + ty::Uint(UintTy::U64) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u64(x) + } + ty::Uint(UintTy::Usize) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_target_usize(x.try_into().unwrap(), this) + } + // Functions with no declared return type (i.e., the default return) + // have the output_type `Tuple([])`. + ty::Tuple(t_list) if t_list.len() == 0 => { + unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; + return Ok(ImmTy::uninit(dest.layout)); + } + _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name), + }; + Ok(ImmTy::from_scalar(scalar, dest.layout)) + } + + /// Get the pointer to the function of the specified name in the shared object file, + /// if it exists. The function must be in the shared object file specified: we do *not* + /// return pointers to functions in dependencies of the library. + fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { + let this = self.eval_context_mut(); + // Try getting the function from the shared library. + // On windows `_lib_path` will be unused, hence the name starting with `_`. + let (lib, _lib_path) = this.machine.native_lib.as_ref().unwrap(); + let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { + match lib.get(link_name.as_str().as_bytes()) { + Ok(x) => x, + Err(_) => { + return None; + } + } + }; + + // FIXME: this is a hack! + // The `libloading` crate will automatically load system libraries like `libc`. + // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 + // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the + // library if it can't find the symbol in the library itself. + // So, in order to check if the function was actually found in the specified + // `machine.external_so_lib` we need to check its `dli_fname` and compare it to + // the specified SO file path. + // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, + // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 + // using the `libc` crate where this interface is public. + let mut info = std::mem::MaybeUninit::::uninit(); + unsafe { + if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { + if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + != _lib_path.to_str().unwrap() + { + return None; + } + } + } + // Return a pointer to the function. + Some(CodePtr(*func.deref() as *mut _)) + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Call the native host function, with supplied arguments. + /// Needs to convert all the arguments from their Miri representations to + /// a native form (through `libffi` call). + /// Then, convert the return value from the native form into something that + /// can be stored in Miri's internal memory. + fn call_native_fn( + &mut self, + link_name: Symbol, + dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx, Provenance>], + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + // Get the pointer to the function in the shared object file if it exists. + let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) { + Some(ptr) => ptr, + None => { + // Shared object file does not export this function -- try the shims next. + return Ok(false); + } + }; + + // Get the function arguments, and convert them to `libffi`-compatible form. + let mut libffi_args = Vec::::with_capacity(args.len()); + for arg in args.iter() { + if !matches!(arg.layout.abi, Abi::Scalar(_)) { + throw_unsup_format!("only scalar argument types are support for native calls") + } + libffi_args.push(imm_to_carg(this.read_immediate(arg)?, this)?); + } + + // Convert them to `libffi::high::Arg` type. + let libffi_args = libffi_args + .iter() + .map(|arg| arg.arg_downcast()) + .collect::>>(); + + // Call the function and store output, depending on return type in the function signature. + let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; + this.write_immediate(*ret, dest)?; + Ok(true) + } +} + +#[derive(Debug, Clone)] +/// Enum of supported arguments to external C functions. +// We introduce this enum instead of just calling `ffi::arg` and storing a list +// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference +// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html +// and we need to store a copy of the value, and pass a reference to this copy to C instead. +enum CArg { + /// 8-bit signed integer. + Int8(i8), + /// 16-bit signed integer. + Int16(i16), + /// 32-bit signed integer. + Int32(i32), + /// 64-bit signed integer. + Int64(i64), + /// isize. + ISize(isize), + /// 8-bit unsigned integer. + UInt8(u8), + /// 16-bit unsigned integer. + UInt16(u16), + /// 32-bit unsigned integer. + UInt32(u32), + /// 64-bit unsigned integer. + UInt64(u64), + /// usize. + USize(usize), +} + +impl<'a> CArg { + /// Convert a `CArg` to a `libffi` argument type. + fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { + match self { + CArg::Int8(i) => ffi::arg(i), + CArg::Int16(i) => ffi::arg(i), + CArg::Int32(i) => ffi::arg(i), + CArg::Int64(i) => ffi::arg(i), + CArg::ISize(i) => ffi::arg(i), + CArg::UInt8(i) => ffi::arg(i), + CArg::UInt16(i) => ffi::arg(i), + CArg::UInt32(i) => ffi::arg(i), + CArg::UInt64(i) => ffi::arg(i), + CArg::USize(i) => ffi::arg(i), + } + } +} + +/// Extract the scalar value from the result of reading a scalar from the machine, +/// and convert it to a `CArg`. +fn imm_to_carg<'tcx>( + v: ImmTy<'tcx, Provenance>, + cx: &impl HasDataLayout, +) -> InterpResult<'tcx, CArg> { + Ok(match v.layout.ty.kind() { + // If the primitive provided can be converted to a type matching the type pattern + // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. + // the ints + ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?), + ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?), + ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?), + ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?), + ty::Int(IntTy::Isize) => + CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()), + // the uints + ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?), + ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?), + ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?), + ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?), + ty::Uint(UintTy::Usize) => + CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()), + _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty), + }) +} diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 9082d13da8404..6fcfb69391536 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -228,7 +228,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(Pointer::null()); } @@ -241,7 +241,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let erange = this.eval_libc("ERANGE"); this.set_last_error(erange)?; } - Err(e) => this.set_last_error_from_io_error(e.kind())?, + Err(e) => this.set_last_error_from_io_error(e)?, } Ok(Pointer::null()) @@ -255,7 +255,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`chdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -263,7 +263,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match env::set_current_dir(path) { Ok(()) => Ok(0), Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index e536b78d1c0bc..a53cd607ef063 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -312,7 +312,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -394,7 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(read_bytes) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index aaf47248af629..08f64e499b558 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -310,7 +310,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(dest)?; } Some(len) => { - let res = this.realloc(ptr, len, MiriMemoryKind::C)?; + let res = this.realloc(ptr, len)?; this.write_pointer(res, dest)?; } } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 058747916c0ac..eb241556a5691 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -137,37 +137,28 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx &mut self, file_type: std::io::Result, ) -> InterpResult<'tcx, i32> { + #[cfg(unix)] + use std::os::unix::fs::FileTypeExt; + let this = self.eval_context_mut(); match file_type { Ok(file_type) => { - if file_type.is_dir() { - Ok(this.eval_libc("DT_DIR").to_u8()?.into()) - } else if file_type.is_file() { - Ok(this.eval_libc("DT_REG").to_u8()?.into()) - } else if file_type.is_symlink() { - Ok(this.eval_libc("DT_LNK").to_u8()?.into()) - } else { + match () { + _ if file_type.is_dir() => Ok(this.eval_libc("DT_DIR").to_u8()?.into()), + _ if file_type.is_file() => Ok(this.eval_libc("DT_REG").to_u8()?.into()), + _ if file_type.is_symlink() => Ok(this.eval_libc("DT_LNK").to_u8()?.into()), // Certain file types are only supported when the host is a Unix system. - // (i.e. devices and sockets) If it is, check those cases, if not, fall back to - // DT_UNKNOWN sooner. - #[cfg(unix)] - { - use std::os::unix::fs::FileTypeExt; - if file_type.is_block_device() { - Ok(this.eval_libc("DT_BLK").to_u8()?.into()) - } else if file_type.is_char_device() { - Ok(this.eval_libc("DT_CHR").to_u8()?.into()) - } else if file_type.is_fifo() { - Ok(this.eval_libc("DT_FIFO").to_u8()?.into()) - } else if file_type.is_socket() { - Ok(this.eval_libc("DT_SOCK").to_u8()?.into()) - } else { - Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()) - } - } - #[cfg(not(unix))] - Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()) + _ if file_type.is_block_device() => + Ok(this.eval_libc("DT_BLK").to_u8()?.into()), + #[cfg(unix)] + _ if file_type.is_char_device() => Ok(this.eval_libc("DT_CHR").to_u8()?.into()), + #[cfg(unix)] + _ if file_type.is_fifo() => Ok(this.eval_libc("DT_FIFO").to_u8()?.into()), + #[cfg(unix)] + _ if file_type.is_socket() => Ok(this.eval_libc("DT_SOCK").to_u8()?.into()), + // Fallback + _ => Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), } } Err(e) => @@ -387,7 +378,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -443,7 +434,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -474,7 +465,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -775,7 +766,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -803,7 +794,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -831,7 +822,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -868,7 +859,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_target_usize(id, this)) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(Scalar::null_ptr(this)) } } @@ -956,7 +947,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None } Some(Err(e)) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; None } }; @@ -1308,13 +1299,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(path_bytes.len().try_into().unwrap()) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } } - #[cfg_attr(not(unix), allow(unused))] fn isatty( &mut self, miri_fd: &OpTy<'tcx, Provenance>, @@ -1392,7 +1382,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_maybe_pointer(dest, this)) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(Scalar::from_target_usize(0, this)) } } @@ -1467,8 +1457,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[cfg(unix)] { use std::os::unix::fs::OpenOptionsExt; - fopts.mode(0o600); // Do not allow others to read or modify this file. + fopts.mode(0o600); fopts.custom_flags(libc::O_EXCL); } #[cfg(windows)] @@ -1513,7 +1503,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => { // "On error, -1 is returned, and errno is set to // indicate the error" - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; return Ok(-1); } }, @@ -1592,7 +1582,7 @@ impl FileMetadata { let metadata = match metadata { Ok(metadata) => metadata, Err(e) => { - ecx.set_last_error_from_io_error(e.kind())?; + ecx.set_last_error_from_io_error(e)?; return Ok(None); } }; diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index ecf82f26a5528..7cd749a41072e 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -117,6 +117,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). id if id == sys_getrandom => { + // Used by getrandom 0.1 // The first argument is the syscall id, so skip over it. if args.len() < 4 { throw_ub_format!( @@ -124,7 +125,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { args.len() ); } - getrandom(this, &args[1], &args[2], &args[3], dest)?; + + let ptr = this.read_pointer(&args[1])?; + let len = this.read_target_usize(&args[2])?; + // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, + // neither of which have any effect on our current PRNG. + // See for a discussion of argument sizes. + let _flags = this.read_scalar(&args[3])?.to_i32(); + + this.gen_random(ptr, len)?; + this.write_scalar(Scalar::from_target_usize(len, this), dest)?; } // `futex` is used by some synchronization primitives. id if id == sys_futex => { @@ -196,24 +206,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(EmulateItemResult::NeedsJumping) } } - -// Shims the linux `getrandom` syscall. -fn getrandom<'tcx>( - this: &mut MiriInterpCx<'_, 'tcx>, - ptr: &OpTy<'tcx, Provenance>, - len: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, -) -> InterpResult<'tcx> { - let ptr = this.read_pointer(ptr)?; - let len = this.read_target_usize(len)?; - - // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, - // neither of which have any effect on our current PRNG. - // See for a discussion of argument sizes. - let _flags = this.read_scalar(flags)?.to_i32(); - - this.gen_random(ptr, len)?; - this.write_scalar(Scalar::from_target_usize(len, this), dest)?; - Ok(()) -} diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index f52dc23656db6..0254735ac138c 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -42,9 +42,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let map_shared = this.eval_libc_i32("MAP_SHARED"); let map_fixed = this.eval_libc_i32("MAP_FIXED"); - // This is a horrible hack, but on MacOS the guard page mechanism uses mmap + // This is a horrible hack, but on MacOS and Solaris the guard page mechanism uses mmap // in a way we do not support. We just give it the return value it expects. - if this.frame_in_std() && this.tcx.sess.target.os == "macos" && (flags & map_fixed) != 0 { + if this.frame_in_std() + && matches!(&*this.tcx.sess.target.os, "macos" | "solaris") + && (flags & map_fixed) != 0 + { return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index c01ae0ecb908b..c216d8afd7733 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -2,7 +2,6 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use crate::*; -use shims::EmulateItemResult; pub fn is_dyn_sym(_name: &str) -> bool { false @@ -26,6 +25,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } + "stack_getbounds" => { + let [stack] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?; + + this.write_int_fields_named( + &[ + ("ss_sp", this.machine.stack_addr.into()), + ("ss_size", this.machine.stack_size.into()), + // field set to 0 means not in an alternate signal stack + // https://docs.oracle.com/cd/E86824_01/html/E54766/stack-getbounds-3c.html + ("ss_flags", 0), + ], + &stack, + )?; + + this.write_null(dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), } Ok(EmulateItemResult::NeedsJumping) diff --git a/src/tools/miri/src/shims/windows/env.rs b/src/tools/miri/src/shims/windows/env.rs index b3bc5d4d85235..0e52959b762e6 100644 --- a/src/tools/miri/src/shims/windows/env.rs +++ b/src/tools/miri/src/shims/windows/env.rs @@ -153,7 +153,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(Scalar::from_u32(0)); } @@ -166,7 +166,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_path_to_wide_str(&cwd, buf, size)?, ))); } - Err(e) => this.set_last_error_from_io_error(e.kind())?, + Err(e) => this.set_last_error_from_io_error(e)?, } Ok(Scalar::from_u32(0)) } @@ -185,7 +185,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(this.eval_windows("c", "FALSE")); } @@ -193,7 +193,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match env::set_current_dir(path) { Ok(()) => Ok(this.eval_windows("c", "TRUE")), Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(this.eval_windows("c", "FALSE")) } } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index dba5b7a906f91..086abf19c5cff 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -5,10 +5,9 @@ use std::path::{self, Path, PathBuf}; use std::str; use rustc_span::Symbol; -use rustc_target::abi::Size; +use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; -use crate::shims::alloc::EvalContextExt as _; use crate::shims::os_str::bytes_to_os_str; use crate::shims::windows::*; use crate::*; @@ -225,7 +224,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let filename = this.read_path_from_wide_str(filename)?; let result = match win_absolute(&filename)? { Err(err) => { - this.set_last_error_from_io_error(err.kind())?; + this.set_last_error_from_io_error(err)?; Scalar::from_u32(0) // return zero upon failure } Ok(abs_filename) => { @@ -248,8 +247,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size)?; let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY let zero_init = (flags & heap_zero_memory) == heap_zero_memory; - let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?; - this.write_pointer(res, dest)?; + // Alignment is twice the pointer size. + // Source: + let align = this.tcx.pointer_size().bytes().strict_mul(2); + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::WinHeap.into(), + )?; + if zero_init { + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + )?; + } + this.write_pointer(ptr, dest)?; } "HeapFree" => { let [handle, flags, ptr] = @@ -257,23 +269,41 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; - this.free(ptr, MiriMemoryKind::WinHeap)?; + // "This pointer can be NULL." It doesn't say what happens then, but presumably nothing. + // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree) + if !this.ptr_is_null(ptr)? { + this.deallocate_ptr(ptr, None, MiriMemoryKind::WinHeap.into())?; + } this.write_scalar(Scalar::from_i32(1), dest)?; } "HeapReAlloc" => { - let [handle, flags, ptr, size] = + let [handle, flags, old_ptr, size] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; - let ptr = this.read_pointer(ptr)?; + let old_ptr = this.read_pointer(old_ptr)?; let size = this.read_target_usize(size)?; - let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?; - this.write_pointer(res, dest)?; + let align = this.tcx.pointer_size().bytes().strict_mul(2); // same as above + // The docs say that `old_ptr` must come from an earlier HeapAlloc or HeapReAlloc, + // so unlike C `realloc` we do *not* allow a NULL here. + // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc) + let new_ptr = this.reallocate_ptr( + old_ptr, + None, + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::WinHeap.into(), + )?; + this.write_pointer(new_ptr, dest)?; } "LocalFree" => { let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; - this.free(ptr, MiriMemoryKind::WinLocal)?; + // "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) + if !this.ptr_is_null(ptr)? { + this.deallocate_ptr(ptr, None, MiriMemoryKind::WinLocal.into())?; + } this.write_null(dest)?; } @@ -513,6 +543,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); } "SystemFunction036" => { + // used by getrandom 0.1 // This is really 'RtlGenRandom'. let [ptr, len] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; @@ -522,6 +553,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_bool(true), dest)?; } "ProcessPrng" => { + // used by `std` let [ptr, len] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; @@ -530,6 +562,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_i32(1), dest)?; } "BCryptGenRandom" => { + // used by getrandom 0.2 let [algorithm, ptr, len, flags] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let algorithm = this.read_scalar(algorithm)?; diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 2639d29b73de6..83f3e4c919b9a 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -10,6 +10,7 @@ import re import subprocess import sys +import argparse CGREEN = '\33[32m' CBOLD = '\33[1m' @@ -25,8 +26,8 @@ def cargo_miri(cmd, quiet = True): args = ["cargo", "miri", cmd] + CARGO_EXTRA_FLAGS if quiet: args += ["-q"] - if 'MIRI_TEST_TARGET' in os.environ: - args += ["--target", os.environ['MIRI_TEST_TARGET']] + if ARGS.target: + args += ["--target", ARGS.target] return args def normalize_stdout(str): @@ -35,7 +36,7 @@ def normalize_stdout(str): return str def check_output(actual, path, name): - if os.environ.get("RUSTC_BLESS", "0") != "0": + if ARGS.bless: # Write the output only if bless is set open(path, mode='w').write(actual) return True @@ -133,7 +134,7 @@ def test_cargo_miri_run(): def test_cargo_miri_test(): # rustdoc is not run on foreign targets - is_foreign = 'MIRI_TEST_TARGET' in os.environ + is_foreign = ARGS.target is not None default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref" filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref" @@ -182,16 +183,22 @@ def test_cargo_miri_test(): env={'MIRIFLAGS': "-Zmiri-permissive-provenance"}, ) +args_parser = argparse.ArgumentParser(description='`cargo miri` testing') +args_parser.add_argument('--target', help='the target to test') +args_parser.add_argument('--bless', help='bless the reference files', action='store_true') +ARGS = args_parser.parse_args() + os.chdir(os.path.dirname(os.path.realpath(__file__))) os.environ["CARGO_TARGET_DIR"] = "target" # this affects the location of the target directory that we need to check os.environ["RUST_TEST_NOCAPTURE"] = "0" # this affects test output, so make sure it is not set os.environ["RUST_TEST_THREADS"] = "1" # avoid non-deterministic output due to concurrent test runs -target_str = " for target {}".format(os.environ['MIRI_TEST_TARGET']) if 'MIRI_TEST_TARGET' in os.environ else "" +target_str = " for target {}".format(ARGS.target) if ARGS.target else "" print(CGREEN + CBOLD + "## Running `cargo miri` tests{}".format(target_str) + CEND) test_cargo_miri_run() test_cargo_miri_test() + # Ensure we did not create anything outside the expected target dir. for target_dir in ["target", "custom-run", "custom-test", "config-cli"]: if os.listdir(target_dir) != ["miri"]: diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index 3dd5eb9b91c55..c73d13a4620c2 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -17,12 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "backtrace" version = "0.3.71" @@ -50,12 +44,6 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "bytes" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - [[package]] name = "cc" version = "1.0.96" @@ -141,16 +129,6 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -192,7 +170,6 @@ dependencies = [ "libc", "num_cpus", "page_size", - "rand", "tempfile", "tokio", "windows-sys 0.52.0", @@ -233,41 +210,12 @@ dependencies = [ "winapi", ] -[[package]] -name = "parking_lot" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.5", -] - [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "proc-macro2" version = "1.0.81" @@ -286,45 +234,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.14", -] - -[[package]] -name = "redox_syscall" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -344,27 +253,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "socket2" version = "0.5.7" @@ -405,13 +293,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", - "bytes", "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index 9f28e4d169f4a..1894f53ce49c0 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -15,11 +15,10 @@ tempfile = "3" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } -rand = { version = "0.8", features = ["small_rng"] } [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies] page_size = "0.6" -tokio = { version = "1.24", features = ["full"] } +tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = [ "Win32_Foundation", "Win32_System_Threading" ] } diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs index 94ca3496ed948..f22f17be0df56 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_cond twice fails, even without a check for number validity diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs similarity index 92% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs index b5427d55eb07a..d0ccab4de5b58 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@ignore-target-apple: Our macOS condattr don't have any fields so we do not notice this. /// Test that destroying a pthread_condattr twice fails, even without a check for number validity diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs index 7e6f490bb3dd4..9cd0a35d36e94 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: the main thread terminated without waiting for all remaining threads // Check that we terminate the program when the main thread terminates. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs index e1d3704af7c0b..96dd99e884428 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //! The thread function must have exactly one argument. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs index 7408634db528f..d8fbc68d344cd 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //! The thread function must have exactly one argument. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs index 0b810dc8c7212..e89d7a9f02b92 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_detached.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining a detached thread is undefined behavior. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs index 04ca4bbb3f611..cbad743ca5633 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_joined.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining an already joined thread is undefined behavior. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs index 7576518216372..002498e6c8263 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_main.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining the main thread is undefined behavior. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs index 966f416eeac7e..f5b687a623f73 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining the same thread from multiple threads is undefined behavior. diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs index 0c25c690f3721..4bc1c82a254c5 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_join_self.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs similarity index 90% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs index 8b2510733831f..0a494c53b4b5c 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // // Check that if we pass NULL attribute, then we get the default mutex type. diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs index 60d56d41fd986..0328115c63759 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: deadlock use std::cell::UnsafeCell; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs similarity index 91% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs index f443768819f96..1038b8988f99f 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // // Check that if we do not set the mutex type, it is the default. diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs similarity index 92% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs index ec3965c7574eb..e474712cfd994 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { unsafe { diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs index 622c3eaeae30d..46f0c5f8d72ee 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_mutex twice fails, even without a check for number validity diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs similarity index 92% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs index 5ea09fa5aac3d..f311934e28bb3 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { unsafe { diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs similarity index 92% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs index 8ce7542edb87f..2b5886dc16375 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { unsafe { diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs index b56775252e4b4..686a394f7cb9b 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs similarity index 91% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs index 474a277516d94..51f00d62b75b5 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs similarity index 83% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs index 603580ff58abd..fa4575bc1d4d1 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs similarity index 83% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs index ae44f22d146ca..e734a62bca8db 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs similarity index 89% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs index 800986f7506c0..e96f7fc680382 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs similarity index 82% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs index 782c95b6d2e3c..dffeee2b794ec 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs index 1b498ad8fcdb4..328372b22ef93 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs similarity index 78% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs index 05f7e7a06c57f..ced6b7a4f613c 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs index 0f02c3231a688..4174751926d85 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: deadlock use std::cell::UnsafeCell; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs similarity index 82% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs index 538f14ef89f20..099b8dcd106ac 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs index 10be5b3375230..43b3ab09bb237 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: deadlock use std::cell::UnsafeCell; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs similarity index 82% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs index 2c963d36510e6..2704ff154413c 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs similarity index 93% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs index dd099474d8fed..9a2cd09f083e7 100644 --- a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.stderr rename to src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.rs b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs similarity index 90% rename from src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.rs rename to src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs index 2b9e7a34d6543..a1895feb957bc 100644 --- a/src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc env support on Windows use std::env; use std::thread; diff --git a/src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.stderr b/src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/env-set_var-data-race.stderr rename to src/tools/miri/tests/fail-dep/libc/env-set_var-data-race.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/fs/close_stdout.rs b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs similarity index 82% rename from src/tools/miri/tests/fail-dep/shims/fs/close_stdout.rs rename to src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs index 09da8509af412..42b7e2b7838cb 100644 --- a/src/tools/miri/tests/fail-dep/shims/fs/close_stdout.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows //@compile-flags: -Zmiri-disable-isolation // FIXME: standard handles cannot be closed (https://github.com/rust-lang/rust/issues/40032) diff --git a/src/tools/miri/tests/fail-dep/shims/fs/close_stdout.stderr b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/fs/close_stdout.stderr rename to src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.rs b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs similarity index 83% rename from src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.rs rename to src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs index a45f805696d49..3c62015a051ad 100644 --- a/src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; diff --git a/src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.stderr b/src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/fs/isolated_stdin.stderr rename to src/tools/miri/tests/fail-dep/libc/fs/isolated_stdin.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs similarity index 86% rename from src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs rename to src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs index ba9f404d7c9ac..6d951a3a7b387 100644 --- a/src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mkstemp on Windows //@compile-flags: -Zmiri-disable-isolation fn main() { diff --git a/src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.stderr b/src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/fs/mkstemp_immutable_arg.stderr rename to src/tools/miri/tests/fail-dep/libc/fs/mkstemp_immutable_arg.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.rs b/src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.rs similarity index 83% rename from src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.rs rename to src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.rs index 073fca4712e9a..624f584a0c85e 100644 --- a/src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; diff --git a/src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.stderr b/src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/fs/read_from_stdout.stderr rename to src/tools/miri/tests/fail-dep/libc/fs/read_from_stdout.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs similarity index 89% rename from src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs rename to src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs index ae231d4be667e..d783967f95968 100644 --- a/src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows //@compile-flags: -Zmiri-disable-isolation fn main() { diff --git a/src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.stderr b/src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/fs/unix_open_missing_required_mode.stderr rename to src/tools/miri/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.rs b/src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.rs similarity index 80% rename from src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.rs rename to src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.rs index d039ad718d339..683c55e90e182 100644 --- a/src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.rs +++ b/src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows fn main() -> std::io::Result<()> { let bytes = b"hello"; diff --git a/src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.stderr b/src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/fs/write_to_stdin.stderr rename to src/tools/miri/tests/fail-dep/libc/fs/write_to_stdin.stderr diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs new file mode 100644 index 0000000000000..3298d61c8e8c4 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs @@ -0,0 +1,7 @@ +fn main() { + unsafe { + let ptr = libc::malloc(0); + libc::free(ptr); + libc::free(ptr); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr new file mode 100644 index 0000000000000..6437c9dbeb44a --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling + | + = 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: ALLOC was allocated here: + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | let ptr = libc::malloc(0); + | ^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/malloc_zero_double_free.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs new file mode 100644 index 0000000000000..b09a74f41d7dd --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs @@ -0,0 +1,5 @@ +fn main() { + unsafe { + let _ptr = libc::malloc(0); //~ERROR: memory leak + } +} diff --git a/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr new file mode 100644 index 0000000000000..65ce0dcdcddea --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 1), allocated here: + --> $DIR/malloc_zero_memory_leak.rs:LL:CC + | +LL | let _ptr = libc::malloc(0); + | ^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/malloc_zero_memory_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail-dep/shims/memchr_null.rs b/src/tools/miri/tests/fail-dep/libc/memchr_null.rs similarity index 77% rename from src/tools/miri/tests/fail-dep/shims/memchr_null.rs rename to src/tools/miri/tests/fail-dep/libc/memchr_null.rs index 6bc7af7e6bf77..672cc10cd6304 100644 --- a/src/tools/miri/tests/fail-dep/shims/memchr_null.rs +++ b/src/tools/miri/tests/fail-dep/libc/memchr_null.rs @@ -1,5 +1,3 @@ -//@ignore-target-windows: No libc on Windows - use std::ptr; // null is explicitly called out as UB in the C docs. diff --git a/src/tools/miri/tests/fail-dep/shims/memchr_null.stderr b/src/tools/miri/tests/fail-dep/libc/memchr_null.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/memchr_null.stderr rename to src/tools/miri/tests/fail-dep/libc/memchr_null.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_null.rs b/src/tools/miri/tests/fail-dep/libc/memcmp_null.rs similarity index 78% rename from src/tools/miri/tests/fail-dep/shims/memcmp_null.rs rename to src/tools/miri/tests/fail-dep/libc/memcmp_null.rs index a4e0034c40bdc..066af4a8ae3c9 100644 --- a/src/tools/miri/tests/fail-dep/shims/memcmp_null.rs +++ b/src/tools/miri/tests/fail-dep/libc/memcmp_null.rs @@ -1,5 +1,3 @@ -//@ignore-target-windows: No libc on Windows - use std::ptr; // null is explicitly called out as UB in the C docs. diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_null.stderr b/src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/memcmp_null.stderr rename to src/tools/miri/tests/fail-dep/libc/memcmp_null.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_zero.rs b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.rs similarity index 90% rename from src/tools/miri/tests/fail-dep/shims/memcmp_zero.rs rename to src/tools/miri/tests/fail-dep/libc/memcmp_zero.rs index f2ddc20056327..e28aa26270e49 100644 --- a/src/tools/miri/tests/fail-dep/shims/memcmp_zero.rs +++ b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.rs @@ -1,4 +1,3 @@ -//@ignore-target-windows: No libc on Windows //@compile-flags: -Zmiri-permissive-provenance // C says that passing "invalid" pointers is UB for all string functions. diff --git a/src/tools/miri/tests/fail-dep/shims/memcmp_zero.stderr b/src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/memcmp_zero.stderr rename to src/tools/miri/tests/fail-dep/libc/memcmp_zero.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/memcpy_zero.rs b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.rs similarity index 88% rename from src/tools/miri/tests/fail-dep/shims/memcpy_zero.rs rename to src/tools/miri/tests/fail-dep/libc/memcpy_zero.rs index 5283fea4cb989..b37ed7df74ffa 100644 --- a/src/tools/miri/tests/fail-dep/shims/memcpy_zero.rs +++ b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.rs @@ -1,4 +1,3 @@ -//@ignore-target-windows: No libc on Windows //@compile-flags: -Zmiri-permissive-provenance // C's memcpy is 0 bytes is UB for some pointers that are allowed in Rust's `copy_nonoverlapping`. diff --git a/src/tools/miri/tests/fail-dep/shims/memcpy_zero.stderr b/src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/memcpy_zero.stderr rename to src/tools/miri/tests/fail-dep/libc/memcpy_zero.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/memrchr_null.rs b/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs similarity index 81% rename from src/tools/miri/tests/fail-dep/shims/memrchr_null.rs rename to src/tools/miri/tests/fail-dep/libc/memrchr_null.rs index b6707d558d8f6..f06336b129959 100644 --- a/src/tools/miri/tests/fail-dep/shims/memrchr_null.rs +++ b/src/tools/miri/tests/fail-dep/libc/memrchr_null.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No `memrchr` on Windows //@ignore-target-apple: No `memrchr` on some apple targets use std::ptr; diff --git a/src/tools/miri/tests/fail-dep/shims/memrchr_null.stderr b/src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/memrchr_null.stderr rename to src/tools/miri/tests/fail-dep/libc/memrchr_null.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.rs b/src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.rs similarity index 90% rename from src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.rs rename to src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.rs index 70f7a6a7cef12..9d55a4935547f 100644 --- a/src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.rs +++ b/src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows #![feature(rustc_private)] diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.stderr b/src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/mmap_invalid_dealloc.stderr rename to src/tools/miri/tests/fail-dep/libc/mmap_invalid_dealloc.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.rs b/src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.rs similarity index 90% rename from src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.rs rename to src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.rs index c97b013ba5a34..05ac232f5d7b4 100644 --- a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.rs +++ b/src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows #![feature(rustc_private)] diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr b/src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr rename to src/tools/miri/tests/fail-dep/libc/mmap_use_after_munmap.stderr diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs b/src/tools/miri/tests/fail-dep/libc/munmap_partial.rs similarity index 94% rename from src/tools/miri/tests/fail-dep/shims/munmap_partial.rs rename to src/tools/miri/tests/fail-dep/libc/munmap_partial.rs index d7aef47235f1e..4386dc71af69c 100644 --- a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs +++ b/src/tools/miri/tests/fail-dep/libc/munmap_partial.rs @@ -1,7 +1,7 @@ //! The man pages for mmap/munmap suggest that it is possible to partly unmap a previously-mapped //! region of address space, but to LLVM that would be partial deallocation, which LLVM does not //! support. So even though the man pages say this sort of use is possible, we must report UB. -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows //@normalize-stderr-test: "size [0-9]+ and alignment" -> "size SIZE and alignment" fn main() { diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr b/src/tools/miri/tests/fail-dep/libc/munmap_partial.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr rename to src/tools/miri/tests/fail-dep/libc/munmap_partial.stderr diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.rs b/src/tools/miri/tests/fail-dep/libc/realloc-zero.rs similarity index 81% rename from src/tools/miri/tests/fail-dep/realloc-zero.rs rename to src/tools/miri/tests/fail-dep/libc/realloc-zero.rs index 1482798e90c08..0e210f313569a 100644 --- a/src/tools/miri/tests/fail-dep/realloc-zero.rs +++ b/src/tools/miri/tests/fail-dep/libc/realloc-zero.rs @@ -1,5 +1,3 @@ -//@ignore-target-windows: No libc on Windows - fn main() { unsafe { let p1 = libc::malloc(20); diff --git a/src/tools/miri/tests/fail-dep/realloc-zero.stderr b/src/tools/miri/tests/fail-dep/libc/realloc-zero.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/realloc-zero.stderr rename to src/tools/miri/tests/fail-dep/libc/realloc-zero.stderr diff --git a/src/tools/miri/tests/fail-dep/unsupported_incomplete_function.rs b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.rs similarity index 87% rename from src/tools/miri/tests/fail-dep/unsupported_incomplete_function.rs rename to src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.rs index 6ef842c9ccb64..cd8422f4afd40 100644 --- a/src/tools/miri/tests/fail-dep/unsupported_incomplete_function.rs +++ b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.rs @@ -1,6 +1,6 @@ //! `signal()` is special on Linux and macOS that it's only supported within libstd. //! The implementation is not complete enough to permit user code to call it. -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No `libc::signal` on Windows //@normalize-stderr-test: "OS `.*`" -> "$$OS" fn main() { diff --git a/src/tools/miri/tests/fail-dep/unsupported_incomplete_function.stderr b/src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr similarity index 100% rename from src/tools/miri/tests/fail-dep/unsupported_incomplete_function.stderr rename to src/tools/miri/tests/fail-dep/libc/unsupported_incomplete_function.stderr diff --git a/src/tools/miri/tests/extern-so/fail/function_not_in_so.rs b/src/tools/miri/tests/native-lib/fail/function_not_in_so.rs similarity index 100% rename from src/tools/miri/tests/extern-so/fail/function_not_in_so.rs rename to src/tools/miri/tests/native-lib/fail/function_not_in_so.rs diff --git a/src/tools/miri/tests/extern-so/fail/function_not_in_so.stderr b/src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr similarity index 100% rename from src/tools/miri/tests/extern-so/fail/function_not_in_so.stderr rename to src/tools/miri/tests/native-lib/fail/function_not_in_so.stderr diff --git a/src/tools/miri/tests/extern-so/libtest.map b/src/tools/miri/tests/native-lib/libtest.map similarity index 100% rename from src/tools/miri/tests/extern-so/libtest.map rename to src/tools/miri/tests/native-lib/libtest.map diff --git a/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs b/src/tools/miri/tests/native-lib/pass/call_extern_c_fn.rs similarity index 100% rename from src/tools/miri/tests/extern-so/pass/call_extern_c_fn.rs rename to src/tools/miri/tests/native-lib/pass/call_extern_c_fn.rs diff --git a/src/tools/miri/tests/extern-so/pass/call_extern_c_fn.stdout b/src/tools/miri/tests/native-lib/pass/call_extern_c_fn.stdout similarity index 100% rename from src/tools/miri/tests/extern-so/pass/call_extern_c_fn.stdout rename to src/tools/miri/tests/native-lib/pass/call_extern_c_fn.stdout diff --git a/src/tools/miri/tests/extern-so/test.c b/src/tools/miri/tests/native-lib/test.c similarity index 100% rename from src/tools/miri/tests/extern-so/test.c rename to src/tools/miri/tests/native-lib/test.c diff --git a/src/tools/miri/tests/panic/unsupported_syscall.rs b/src/tools/miri/tests/panic/unsupported_syscall.rs index 31d666e1d9d80..30f9da5f80e78 100644 --- a/src/tools/miri/tests/panic/unsupported_syscall.rs +++ b/src/tools/miri/tests/panic/unsupported_syscall.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: no `syscall` on Windows //@ignore-target-apple: `syscall` is not supported on macOS //@compile-flags: -Zmiri-panic-on-unsupported diff --git a/src/tools/miri/tests/pass-dep/calloc.rs b/src/tools/miri/tests/pass-dep/calloc.rs deleted file mode 100644 index 62ab63c5fc788..0000000000000 --- a/src/tools/miri/tests/pass-dep/calloc.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ignore-target-windows: No libc on Windows - -use core::slice; - -fn main() { - unsafe { - let p1 = libc::calloc(0, 0); - assert!(p1.is_null()); - - let p2 = libc::calloc(20, 0); - assert!(p2.is_null()); - - let p3 = libc::calloc(0, 20); - assert!(p3.is_null()); - - let p4 = libc::calloc(4, 8); - assert!(!p4.is_null()); - let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8); - assert_eq!(&slice, &[0_u8; 4 * 8]); - libc::free(p4); - } -} diff --git a/src/tools/miri/tests/pass-dep/shims/env-cleanup-data-race.rs b/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs similarity index 93% rename from src/tools/miri/tests/pass-dep/shims/env-cleanup-data-race.rs rename to src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs index 5c29a1b15a792..86a47ba365526 100644 --- a/src/tools/miri/tests/pass-dep/shims/env-cleanup-data-race.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/env-cleanup-data-race.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc env support on Windows use std::ffi::CStr; use std::thread; diff --git a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs index f362caa11dc59..d758168c7c30d 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS. //@compile-flags: -Zmiri-disable-isolation diff --git a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs index 66c0895a5dab0..f1a3c5dc10d07 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS. /// Test that conditional variable timeouts are working properly diff --git a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs index ae874740f2bc3..0eaab9676429d 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //! Test that pthread_key destructors are run in the right order. //! Note that these are *not* used by actual `thread_local!` on Linux! Those use //! `thread_local_dtor::register_dtor` from the stdlib instead. In Miri this hits the fallback path diff --git a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs index 716119a0fc344..1198168795daf 100644 --- a/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs +++ b/src/tools/miri/tests/pass-dep/extra_fn_ptr_gc.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No `dlsym` on Windows //@compile-flags: -Zmiri-permissive-provenance #[path = "../utils/mod.rs"] diff --git a/src/tools/miri/tests/pass-dep/getrandom.rs b/src/tools/miri/tests/pass-dep/getrandom.rs index c0d9296a9a6d9..53de3af76350c 100644 --- a/src/tools/miri/tests/pass-dep/getrandom.rs +++ b/src/tools/miri/tests/pass-dep/getrandom.rs @@ -1,8 +1,9 @@ // mac-os `getrandom_01` does some pointer shenanigans //@compile-flags: -Zmiri-permissive-provenance +//@revisions: isolation no_isolation +//@[no_isolation]compile-flags: -Zmiri-disable-isolation /// Test direct calls of getrandom 0.1 and 0.2. -/// Make sure they work even with isolation enabled (i.e., we do not hit a file-based fallback path). fn main() { let mut data = vec![0; 16]; getrandom_01::getrandom(&mut data).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs b/src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.rs similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs rename to src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.rs diff --git a/src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr b/src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.stderr similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr rename to src/tools/miri/tests/pass-dep/libc/fcntl_f-fullfsync_apple.stderr diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs new file mode 100644 index 0000000000000..d72edd7d9e3f2 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs @@ -0,0 +1,51 @@ +// Symlink tests are separate since they don't in general work on a Windows host. +//@ignore-host-windows: creating symlinks requires admin permissions on Windows +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation + +use std::ffi::CString; +use std::io::{Error, ErrorKind}; +use std::os::unix::ffi::OsStrExt; + +#[path = "../../utils/mod.rs"] +mod utils; + +fn main() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); + let expected_path = path.as_os_str().as_bytes(); + + let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + + // Test that the expected string gets written to a buffer of proper + // length, and that a trailing null byte is not written. + let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let symlink_c_ptr = symlink_c_str.as_ptr(); + + // Make the buf one byte larger than it needs to be, + // and check that the last byte is not overwritten. + let mut large_buf = vec![0xFF; expected_path.len() + 1]; + let res = + unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) }; + // Check that the resolved path was properly written into the buf. + assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); + assert_eq!(large_buf.last(), Some(&0xFF)); + assert_eq!(res, large_buf.len() as isize - 1); + + // Test that the resolved path is truncated if the provided buffer + // is too small. + let mut small_buf = [0u8; 2]; + let res = + unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) }; + assert_eq!(small_buf, &expected_path[..small_buf.len()]); + assert_eq!(res, small_buf.len() as isize); + + // Test that we report a proper error for a missing path. + let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap(); + let res = unsafe { + libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) + }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); +} diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs similarity index 93% rename from src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs rename to src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs index 5185db0b0e29f..088a632427e70 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: no libc on Windows +//@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.stderr similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.stderr rename to src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.stderr diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs similarity index 78% rename from src/tools/miri/tests/pass-dep/shims/libc-fs.rs rename to src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 0dd849a045dfa..80c9757e9c956 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -1,11 +1,11 @@ -//@ignore-target-windows: no libc on Windows +//@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation #![feature(io_error_more)] #![feature(io_error_uncategorized)] use std::ffi::{CStr, CString, OsString}; -use std::fs::{canonicalize, remove_dir_all, remove_file, File}; +use std::fs::{canonicalize, remove_file, File}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; @@ -21,7 +21,6 @@ fn main() { test_ftruncate::(libc::ftruncate); #[cfg(target_os = "linux")] test_ftruncate::(libc::ftruncate64); - test_readlink(); test_file_open_unix_allow_two_args(); test_file_open_unix_needs_three_args(); test_file_open_unix_extra_third_arg(); @@ -38,33 +37,8 @@ fn main() { test_isatty(); } -/// Prepare: compute filename and make sure the file does not exist. -fn prepare(filename: &str) -> PathBuf { - let path = utils::tmp().join(filename); - // Clean the paths for robustness. - remove_file(&path).ok(); - path -} - -/// Prepare directory: compute directory name and make sure it does not exist. -#[allow(unused)] -fn prepare_dir(dirname: &str) -> PathBuf { - let path = utils::tmp().join(&dirname); - // Clean the directory for robustness. - remove_dir_all(&path).ok(); - path -} - -/// Prepare like above, and also write some initial content to the file. -fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { - let path = prepare(filename); - let mut file = File::create(&path).unwrap(); - file.write(content).unwrap(); - path -} - fn test_file_open_unix_allow_two_args() { - let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); + let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); let mut name = path.into_os_string(); name.push("\0"); @@ -73,7 +47,7 @@ fn test_file_open_unix_allow_two_args() { } fn test_file_open_unix_needs_three_args() { - let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); + let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); let mut name = path.into_os_string(); name.push("\0"); @@ -82,7 +56,7 @@ fn test_file_open_unix_needs_three_args() { } fn test_file_open_unix_extra_third_arg() { - let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); + let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); let mut name = path.into_os_string(); name.push("\0"); @@ -106,49 +80,9 @@ fn test_canonicalize_too_long() { assert!(canonicalize(too_long).is_err()); } -fn test_readlink() { - let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); - let expected_path = path.as_os_str().as_bytes(); - - let symlink_path = prepare("miri_test_fs_symlink.txt"); - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - - // Test that the expected string gets written to a buffer of proper - // length, and that a trailing null byte is not written. - let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - let symlink_c_ptr = symlink_c_str.as_ptr(); - - // Make the buf one byte larger than it needs to be, - // and check that the last byte is not overwritten. - let mut large_buf = vec![0xFF; expected_path.len() + 1]; - let res = - unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) }; - // Check that the resolved path was properly written into the buf. - assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); - assert_eq!(large_buf.last(), Some(&0xFF)); - assert_eq!(res, large_buf.len() as isize - 1); - - // Test that the resolved path is truncated if the provided buffer - // is too small. - let mut small_buf = [0u8; 2]; - let res = - unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) }; - assert_eq!(small_buf, &expected_path[..small_buf.len()]); - assert_eq!(res, small_buf.len() as isize); - - // Test that we report a proper error for a missing path. - let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap(); - let res = unsafe { - libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) - }; - assert_eq!(res, -1); - assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); -} - fn test_rename() { - let path1 = prepare("miri_test_libc_fs_source.txt"); - let path2 = prepare("miri_test_libc_fs_rename_destination.txt"); + let path1 = utils::prepare("miri_test_libc_fs_source.txt"); + let path2 = utils::prepare("miri_test_libc_fs_rename_destination.txt"); let file = File::create(&path1).unwrap(); drop(file); @@ -178,7 +112,7 @@ fn test_ftruncate>( // https://docs.rs/libc/latest/i686-unknown-linux-gnu/libc/type.off_t.html let bytes = b"hello"; - let path = prepare("miri_test_libc_fs_ftruncate.txt"); + let path = utils::prepare("miri_test_libc_fs_ftruncate.txt"); let mut file = File::create(&path).unwrap(); file.write(bytes).unwrap(); file.sync_all().unwrap(); @@ -209,7 +143,7 @@ fn test_ftruncate>( fn test_o_tmpfile_flag() { use std::fs::{create_dir, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; - let dir_path = prepare_dir("miri_test_fs_dir"); + let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); // test that the `O_TMPFILE` custom flag gracefully errors instead of stopping execution assert_eq!( diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.stderr b/src/tools/miri/tests/pass-dep/libc/libc-fs.stderr similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/libc-fs.stderr rename to src/tools/miri/tests/pass-dep/libc/libc-fs.stderr diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs.stdout b/src/tools/miri/tests/pass-dep/libc/libc-fs.stdout similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/libc-fs.stdout rename to src/tools/miri/tests/pass-dep/libc/libc-fs.stdout diff --git a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs similarity index 71% rename from src/tools/miri/tests/pass-dep/shims/libc-misc.rs rename to src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 9644920c499c6..5df3ace7496fd 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -1,33 +1,5 @@ -//@ignore-target-windows: No libc on Windows -//@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(pointer_is_aligned_to)] -#![feature(strict_provenance)] - -use std::mem::{self, transmute}; -use std::ptr; - -/// Tests whether each thread has its own `__errno_location`. -fn test_thread_local_errno() { - #[cfg(any(target_os = "illumos", target_os = "solaris"))] - use libc::___errno as __errno_location; - #[cfg(target_os = "linux")] - use libc::__errno_location; - #[cfg(any(target_os = "macos", target_os = "freebsd"))] - use libc::__error as __errno_location; - - unsafe { - *__errno_location() = 0xBEEF; - std::thread::spawn(|| { - assert_eq!(*__errno_location(), 0); - *__errno_location() = 0xBAD1DEA; - assert_eq!(*__errno_location(), 0xBAD1DEA); - }) - .join() - .unwrap(); - assert_eq!(*__errno_location(), 0xBEEF); - } -} +#![feature(strict_provenance, pointer_is_aligned_to)] +use std::{mem, ptr, slice}; fn test_memcpy() { unsafe { @@ -106,49 +78,75 @@ fn test_strcpy() { } } -#[cfg(target_os = "linux")] -fn test_sigrt() { - let min = libc::SIGRTMIN(); - let max = libc::SIGRTMAX(); - - // "The Linux kernel supports a range of 33 different real-time - // signals, numbered 32 to 64" - assert!(min >= 32); - assert!(max >= 32); - assert!(min <= 64); - assert!(max <= 64); - - // "POSIX.1-2001 requires that an implementation support at least - // _POSIX_RTSIG_MAX (8) real-time signals." - assert!(min < max); - assert!(max - min >= 8) -} +fn test_malloc() { + // Test that small allocations sometimes *are* not very aligned. + let saw_unaligned = (0..64).any(|_| unsafe { + let p = libc::malloc(3); + libc::free(p); + (p as usize) % 4 != 0 // find any that this is *not* 4-aligned + }); + assert!(saw_unaligned); + + unsafe { + let p1 = libc::malloc(20); + p1.write_bytes(0u8, 20); -fn test_dlsym() { - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) }; - assert!(addr as usize == 0); + // old size < new size + let p2 = libc::realloc(p1, 40); + let slice = slice::from_raw_parts(p2 as *const u8, 20); + assert_eq!(&slice, &[0_u8; 20]); - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) }; - assert!(addr as usize != 0); - let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) }; - assert_eq!(isatty(999), 0); - let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); - assert_eq!(errno, libc::EBADF); + // old size == new size + let p3 = libc::realloc(p2, 40); + let slice = slice::from_raw_parts(p3 as *const u8, 20); + assert_eq!(&slice, &[0_u8; 20]); + + // old size > new size + let p4 = libc::realloc(p3, 10); + let slice = slice::from_raw_parts(p4 as *const u8, 10); + assert_eq!(&slice, &[0_u8; 10]); + + libc::free(p4); + } + + unsafe { + // Realloc with size 0 is okay for the null pointer (and acts like `malloc(0)`) + let p2 = libc::realloc(ptr::null_mut(), 0); + assert!(!p2.is_null()); + libc::free(p2); + } + + unsafe { + let p1 = libc::realloc(ptr::null_mut(), 20); + assert!(!p1.is_null()); + + libc::free(p1); + } } -#[cfg(not(any(target_os = "macos", target_os = "illumos")))] -fn test_reallocarray() { +fn test_calloc() { unsafe { - let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); - assert!(!p.is_null()); - libc::free(p); - p = libc::malloc(16); - let r = libc::reallocarray(p, 2, 32); - assert!(!r.is_null()); - libc::free(r); + let p1 = libc::calloc(0, 0); + assert!(!p1.is_null()); + libc::free(p1); + + let p2 = libc::calloc(20, 0); + assert!(!p2.is_null()); + libc::free(p2); + + let p3 = libc::calloc(0, 20); + assert!(!p3.is_null()); + libc::free(p3); + + let p4 = libc::calloc(4, 8); + assert!(!p4.is_null()); + let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8); + assert_eq!(&slice, &[0_u8; 4 * 8]); + libc::free(p4); } } +#[cfg(not(target_os = "windows"))] fn test_memalign() { // A normal allocation. unsafe { @@ -225,18 +223,37 @@ fn test_memalign() { } } -fn main() { - test_thread_local_errno(); - - test_dlsym(); - - test_memcpy(); - test_strcpy(); +#[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "illumos", + target_os = "solaris" +)))] +fn test_reallocarray() { + unsafe { + let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); + assert!(!p.is_null()); + libc::free(p); + p = libc::malloc(16); + let r = libc::reallocarray(p, 2, 32); + assert!(!r.is_null()); + libc::free(r); + } +} +fn main() { + test_malloc(); + test_calloc(); + #[cfg(not(target_os = "windows"))] test_memalign(); - #[cfg(not(any(target_os = "macos", target_os = "illumos")))] + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "illumos", + target_os = "solaris" + )))] test_reallocarray(); - #[cfg(target_os = "linux")] - test_sigrt(); + test_memcpy(); + test_strcpy(); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs new file mode 100644 index 0000000000000..736e0bf8eb7fd --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs @@ -0,0 +1,68 @@ +//@ignore-target-windows: only very limited libc on Windows +//@compile-flags: -Zmiri-disable-isolation +#![feature(io_error_more)] +#![feature(pointer_is_aligned_to)] +#![feature(strict_provenance)] + +use std::mem::transmute; + +/// Tests whether each thread has its own `__errno_location`. +fn test_thread_local_errno() { + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + use libc::___errno as __errno_location; + #[cfg(target_os = "linux")] + use libc::__errno_location; + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + use libc::__error as __errno_location; + + unsafe { + *__errno_location() = 0xBEEF; + std::thread::spawn(|| { + assert_eq!(*__errno_location(), 0); + *__errno_location() = 0xBAD1DEA; + assert_eq!(*__errno_location(), 0xBAD1DEA); + }) + .join() + .unwrap(); + assert_eq!(*__errno_location(), 0xBEEF); + } +} + +#[cfg(target_os = "linux")] +fn test_sigrt() { + let min = libc::SIGRTMIN(); + let max = libc::SIGRTMAX(); + + // "The Linux kernel supports a range of 33 different real-time + // signals, numbered 32 to 64" + assert!(min >= 32); + assert!(max >= 32); + assert!(min <= 64); + assert!(max <= 64); + + // "POSIX.1-2001 requires that an implementation support at least + // _POSIX_RTSIG_MAX (8) real-time signals." + assert!(min < max); + assert!(max - min >= 8) +} + +fn test_dlsym() { + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) }; + assert!(addr as usize == 0); + + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) }; + assert!(addr as usize != 0); + let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) }; + assert_eq!(isatty(999), 0); + let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); + assert_eq!(errno, libc::EBADF); +} + +fn main() { + test_thread_local_errno(); + + test_dlsym(); + + #[cfg(target_os = "linux")] + test_sigrt(); +} diff --git a/src/tools/miri/tests/pass-dep/shims/libc-random.rs b/src/tools/miri/tests/pass-dep/libc/libc-random.rs similarity index 100% rename from src/tools/miri/tests/pass-dep/shims/libc-random.rs rename to src/tools/miri/tests/pass-dep/libc/libc-random.rs diff --git a/src/tools/miri/tests/pass-dep/shims/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs similarity index 98% rename from src/tools/miri/tests/pass-dep/shims/libc-time.rs rename to src/tools/miri/tests/pass-dep/libc/libc-time.rs index 69c75bd8caf3c..ea1e49a072571 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: no libc on Windows +//@ignore-target-windows: no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation use std::ffi::CStr; use std::{env, mem, ptr}; diff --git a/src/tools/miri/tests/pass-dep/shims/mmap.rs b/src/tools/miri/tests/pass-dep/libc/mmap.rs similarity index 99% rename from src/tools/miri/tests/pass-dep/shims/mmap.rs rename to src/tools/miri/tests/pass-dep/libc/mmap.rs index 5acdedc67bf71..a0787c689077d 100644 --- a/src/tools/miri/tests/pass-dep/shims/mmap.rs +++ b/src/tools/miri/tests/pass-dep/libc/mmap.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows //@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance #![feature(strict_provenance)] diff --git a/src/tools/miri/tests/pass-dep/shims/pthread-sync.rs b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs similarity index 99% rename from src/tools/miri/tests/pass-dep/shims/pthread-sync.rs rename to src/tools/miri/tests/pass-dep/libc/pthread-sync.rs index 12d3f2b6f142f..142752621280a 100644 --- a/src/tools/miri/tests/pass-dep/shims/pthread-sync.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // We use `yield` to test specific interleavings, so disable automatic preemption. //@compile-flags: -Zmiri-preemption-rate=0 #![feature(sync_unsafe_cell)] diff --git a/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs similarity index 97% rename from src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs rename to src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs index bc782044d4d80..4c4f542dfd4cd 100644 --- a/src/tools/miri/tests/pass-dep/shims/pthread-threadname.rs +++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::ffi::CStr; #[cfg(not(target_os = "freebsd"))] use std::ffi::CString; diff --git a/src/tools/miri/tests/pass-dep/malloc.rs b/src/tools/miri/tests/pass-dep/malloc.rs deleted file mode 100644 index 35cd137931fd6..0000000000000 --- a/src/tools/miri/tests/pass-dep/malloc.rs +++ /dev/null @@ -1,48 +0,0 @@ -//@ignore-target-windows: No libc on Windows - -use core::{ptr, slice}; - -fn main() { - // Test that small allocations sometimes *are* not very aligned. - let saw_unaligned = (0..64).any(|_| unsafe { - let p = libc::malloc(3); - libc::free(p); - (p as usize) % 4 != 0 // find any that this is *not* 4-aligned - }); - assert!(saw_unaligned); - - unsafe { - // Use calloc for initialized memory - let p1 = libc::calloc(20, 1); - - // old size < new size - let p2 = libc::realloc(p1, 40); - let slice = slice::from_raw_parts(p2 as *const u8, 20); - assert_eq!(&slice, &[0_u8; 20]); - - // old size == new size - let p3 = libc::realloc(p2, 40); - let slice = slice::from_raw_parts(p3 as *const u8, 20); - assert_eq!(&slice, &[0_u8; 20]); - - // old size > new size - let p4 = libc::realloc(p3, 10); - let slice = slice::from_raw_parts(p4 as *const u8, 10); - assert_eq!(&slice, &[0_u8; 10]); - - libc::free(p4); - } - - unsafe { - // Realloc with size 0 is okay for the null pointer - let p2 = libc::realloc(ptr::null_mut(), 0); - assert!(p2.is_null()); - } - - unsafe { - let p1 = libc::realloc(ptr::null_mut(), 20); - assert!(!p1.is_null()); - - libc::free(p1); - } -} diff --git a/src/tools/miri/tests/pass-dep/rand.rs b/src/tools/miri/tests/pass-dep/rand.rs deleted file mode 100644 index 0dce6d86cf481..0000000000000 --- a/src/tools/miri/tests/pass-dep/rand.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@compile-flags: -Zmiri-strict-provenance -use rand::prelude::*; - -// Test using the `rand` crate to generate randomness. -fn main() { - // Fully deterministic seeding. - let mut rng = SmallRng::seed_from_u64(42); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); - - // Try seeding with "real" entropy. - let mut rng = SmallRng::from_entropy(); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); - - // Also try per-thread RNG. - let mut rng = rand::thread_rng(); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); -} diff --git a/src/tools/miri/tests/pass-dep/wcslen.rs b/src/tools/miri/tests/pass-dep/wcslen.rs new file mode 100644 index 0000000000000..c5c9d99247965 --- /dev/null +++ b/src/tools/miri/tests/pass-dep/wcslen.rs @@ -0,0 +1,20 @@ +fn to_c_wchar_t_str(s: &str) -> Vec { + let mut r = Vec::::new(); + for c in s.bytes() { + if c == 0 { + panic!("can't contain a null character"); + } + if c >= 128 { + panic!("only ASCII supported"); + } + r.push(c.into()); + } + r.push(0); + r +} + +pub fn main() { + let s = to_c_wchar_t_str("Rust"); + let len = unsafe { libc::wcslen(s.as_ptr()) }; + assert_eq!(len, 4); +} diff --git a/src/tools/miri/tests/pass/shims/fs-symlink.rs b/src/tools/miri/tests/pass/shims/fs-symlink.rs new file mode 100644 index 0000000000000..4d5103b24c32d --- /dev/null +++ b/src/tools/miri/tests/pass/shims/fs-symlink.rs @@ -0,0 +1,50 @@ +// Symlink tests are separate since they don't in general work on a Windows host. +//@ignore-host-windows: creating symlinks requires admin permissions on Windows +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation + +use std::fs::{read_link, remove_file, File}; +use std::io::{Read, Result}; +use std::path::Path; + +#[path = "../../utils/mod.rs"] +mod utils; + +fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> { + // Test that the file metadata is correct. + let metadata = path.metadata()?; + // `path` should point to a file. + assert!(metadata.is_file()); + // The size of the file must be equal to the number of written bytes. + assert_eq!(bytes.len() as u64, metadata.len()); + Ok(()) +} + +fn main() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); + let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); + + // Creating a symbolic link should succeed. + #[cfg(unix)] + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + #[cfg(windows)] + std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap(); + // Test that the symbolic link has the same contents as the file. + let mut symlink_file = File::open(&symlink_path).unwrap(); + let mut contents = Vec::new(); + symlink_file.read_to_end(&mut contents).unwrap(); + assert_eq!(bytes, contents.as_slice()); + + // Test that metadata of a symbolic link (i.e., the file it points to) is correct. + check_metadata(bytes, &symlink_path).unwrap(); + // Test that the metadata of a symbolic link is correct when not following it. + assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink()); + // Check that we can follow the link. + assert_eq!(read_link(&symlink_path).unwrap(), path); + // Removing symbolic link should succeed. + remove_file(&symlink_path).unwrap(); + + // Removing file should succeed. + remove_file(&path).unwrap(); +} diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 8a500b857bca6..35980fad15dd0 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -1,21 +1,17 @@ //@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation -// If this test is failing for you locally, you can try -// 1. Deleting the files `/tmp/miri_*` -// 2. Setting `MIRI_TEMP` or `TMPDIR` to a different directory, without the `miri_*` files - #![feature(io_error_more)] #![feature(io_error_uncategorized)] use std::collections::HashMap; use std::ffi::OsString; use std::fs::{ - canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, - File, OpenOptions, + canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, File, + OpenOptions, }; use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; -use std::path::{Path, PathBuf}; +use std::path::Path; #[path = "../../utils/mod.rs"] mod utils; @@ -29,7 +25,6 @@ fn main() { test_metadata(); test_file_set_len(); test_file_sync(); - test_symlink(); test_errors(); test_rename(); test_directory(); @@ -37,30 +32,6 @@ fn main() { test_from_raw_os_error(); } -/// Prepare: compute filename and make sure the file does not exist. -fn prepare(filename: &str) -> PathBuf { - let path = utils::tmp().join(filename); - // Clean the paths for robustness. - remove_file(&path).ok(); - path -} - -/// Prepare directory: compute directory name and make sure it does not exist. -fn prepare_dir(dirname: &str) -> PathBuf { - let path = utils::tmp().join(&dirname); - // Clean the directory for robustness. - remove_dir_all(&path).ok(); - path -} - -/// Prepare like above, and also write some initial content to the file. -fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { - let path = prepare(filename); - let mut file = File::create(&path).unwrap(); - file.write(content).unwrap(); - path -} - fn test_path_conversion() { let tmp = utils::tmp(); assert!(tmp.is_absolute(), "{:?} is not absolute", tmp); @@ -69,7 +40,7 @@ fn test_path_conversion() { fn test_file() { let bytes = b"Hello, World!\n"; - let path = prepare("miri_test_fs_file.txt"); + let path = utils::prepare("miri_test_fs_file.txt"); // Test creating, writing and closing a file (closing is tested when `file` is dropped). let mut file = File::create(&path).unwrap(); @@ -96,7 +67,7 @@ fn test_file() { fn test_file_clone() { let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_file_clone.txt", bytes); // Cloning a file should be successful. let file = File::open(&path).unwrap(); @@ -111,7 +82,7 @@ fn test_file_clone() { } fn test_file_create_new() { - let path = prepare("miri_test_fs_file_create_new.txt"); + let path = utils::prepare("miri_test_fs_file_create_new.txt"); // Creating a new file that doesn't yet exist should succeed. OpenOptions::new().write(true).create_new(true).open(&path).unwrap(); @@ -129,7 +100,7 @@ fn test_file_create_new() { fn test_seek() { let bytes = b"Hello, entire World!\n"; - let path = prepare_with_content("miri_test_fs_seek.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_seek.txt", bytes); let mut file = File::open(&path).unwrap(); let mut contents = Vec::new(); @@ -168,7 +139,7 @@ fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> { fn test_metadata() { let bytes = b"Hello, meta-World!\n"; - let path = prepare_with_content("miri_test_fs_metadata.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_metadata.txt", bytes); // Test that metadata of an absolute path is correct. check_metadata(bytes, &path).unwrap(); @@ -182,7 +153,7 @@ fn test_metadata() { fn test_file_set_len() { let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_set_len.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_set_len.txt", bytes); // Test extending the file let mut file = OpenOptions::new().read(true).write(true).open(&path).unwrap(); @@ -208,7 +179,7 @@ fn test_file_set_len() { fn test_file_sync() { let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_sync.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_sync.txt", bytes); // Test that we can call sync_data and sync_all (can't readily test effects of this operation) let file = OpenOptions::new().write(true).open(&path).unwrap(); @@ -223,38 +194,9 @@ fn test_file_sync() { remove_file(&path).unwrap(); } -fn test_symlink() { - let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); - let symlink_path = prepare("miri_test_fs_symlink.txt"); - - // Creating a symbolic link should succeed. - #[cfg(unix)] - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - #[cfg(windows)] - std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap(); - // Test that the symbolic link has the same contents as the file. - let mut symlink_file = File::open(&symlink_path).unwrap(); - let mut contents = Vec::new(); - symlink_file.read_to_end(&mut contents).unwrap(); - assert_eq!(bytes, contents.as_slice()); - - // Test that metadata of a symbolic link (i.e., the file it points to) is correct. - check_metadata(bytes, &symlink_path).unwrap(); - // Test that the metadata of a symbolic link is correct when not following it. - assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink()); - // Check that we can follow the link. - assert_eq!(read_link(&symlink_path).unwrap(), path); - // Removing symbolic link should succeed. - remove_file(&symlink_path).unwrap(); - - // Removing file should succeed. - remove_file(&path).unwrap(); -} - fn test_errors() { let bytes = b"Hello, World!\n"; - let path = prepare("miri_test_fs_errors.txt"); + let path = utils::prepare("miri_test_fs_errors.txt"); // The following tests also check that the `__errno_location()` shim is working properly. // Opening a non-existing file should fail with a "not found" error. @@ -269,8 +211,8 @@ fn test_errors() { fn test_rename() { // Renaming a file should succeed. - let path1 = prepare("miri_test_fs_rename_source.txt"); - let path2 = prepare("miri_test_fs_rename_destination.txt"); + let path1 = utils::prepare("miri_test_fs_rename_source.txt"); + let path2 = utils::prepare("miri_test_fs_rename_destination.txt"); let file = File::create(&path1).unwrap(); drop(file); @@ -289,7 +231,7 @@ fn test_rename() { } fn test_canonicalize() { - let dir_path = prepare_dir("miri_test_fs_dir"); + let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); let path = dir_path.join("test_file"); drop(File::create(&path).unwrap()); @@ -301,7 +243,7 @@ fn test_canonicalize() { } fn test_directory() { - let dir_path = prepare_dir("miri_test_fs_dir"); + let dir_path = utils::prepare_dir("miri_test_fs_dir"); // Creating a directory should succeed. create_dir(&dir_path).unwrap(); // Test that the metadata of a directory is correct. diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index c9d53927b902a..3a8b098ccbe36 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -27,11 +27,12 @@ pub fn flagsplit(flags: &str) -> Vec { flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() } -// Build the shared object file for testing external C function calls. -fn build_so_for_c_ffi_tests() -> PathBuf { +// Build the shared object file for testing native function calls. +fn build_native_lib() -> PathBuf { let cc = option_env!("CC").unwrap_or("cc"); // Target directory that we can write to. - let so_target_dir = Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-extern-so"); + let so_target_dir = + Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib"); // Create the directory if it does not already exist. std::fs::create_dir_all(&so_target_dir) .expect("Failed to create directory for shared object file"); @@ -41,18 +42,18 @@ fn build_so_for_c_ffi_tests() -> PathBuf { "-shared", "-o", so_file_path.to_str().unwrap(), - "tests/extern-so/test.c", + "tests/native-lib/test.c", // Only add the functions specified in libcode.version to the shared object file. // This is to avoid automatically adding `malloc`, etc. // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/ "-fPIC", - "-Wl,--version-script=tests/extern-so/libtest.map", + "-Wl,--version-script=tests/native-lib/libtest.map", ]) .output() - .expect("failed to generate shared object file for testing external C function calls"); + .expect("failed to generate shared object file for testing native function calls"); if !cc_output.status.success() { panic!( - "error in generating shared object file for testing external C function calls:\n{}", + "error generating shared object file for testing native function calls:\n{}", String::from_utf8_lossy(&cc_output.stderr), ); } @@ -132,13 +133,12 @@ fn run_tests( config.program.args.push("--target".into()); config.program.args.push(target.into()); - // If we're testing the extern-so functionality, then build the shared object file for testing + // If we're testing the native-lib functionality, then build the shared object file for testing // external C function calls and push the relevant compiler flag. - if path.starts_with("tests/extern-so/") { - assert!(cfg!(target_os = "linux")); - let so_file_path = build_so_for_c_ffi_tests(); - let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); - flag.push(so_file_path.into_os_string()); + if path.starts_with("tests/native-lib/") { + let native_lib = build_native_lib(); + let mut flag = std::ffi::OsString::from("-Zmiri-native-lib="); + flag.push(native_lib.into_os_string()); config.program.args.push(flag); } @@ -292,10 +292,10 @@ fn main() -> Result<()> { tmpdir.path(), )?; if cfg!(target_os = "linux") { - ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies, tmpdir.path())?; + ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?; ui( Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled }, - "tests/extern-so/fail", + "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path(), diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index 0242a2280689a..7340908626fdf 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -1,4 +1,5 @@ use std::ffi::OsString; +use std::fs; use std::path::PathBuf; use super::miri_extern; @@ -27,3 +28,26 @@ pub fn tmp() -> PathBuf { // These are host paths. We need to convert them to the target. host_to_target_path(path) } + +/// Prepare: compute filename and make sure the file does not exist. +pub fn prepare(filename: &str) -> PathBuf { + let path = tmp().join(filename); + // Clean the paths for robustness. + fs::remove_file(&path).ok(); + path +} + +/// Prepare like above, and also write some initial content to the file. +pub fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { + let path = prepare(filename); + fs::write(&path, content).unwrap(); + path +} + +/// Prepare directory: compute directory name and make sure it does not exist. +pub fn prepare_dir(dirname: &str) -> PathBuf { + let path = tmp().join(&dirname); + // Clean the directory for robustness. + fs::remove_dir_all(&path).ok(); + path +}