From 0ca3591a6d6896fbbfcea536d14e608a097c53f0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 18:47:37 +0200 Subject: [PATCH 01/29] unix/fs: a bit of cleanup around host-specific code --- src/tools/miri/src/shims/unix/fs.rs | 46 +++++++++++------------------ 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 058747916c0ac..57ae209416c86 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) => @@ -1314,7 +1305,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[cfg_attr(not(unix), allow(unused))] fn isatty( &mut self, miri_fd: &OpTy<'tcx, Provenance>, @@ -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)] From fb8419838715494e950599e55ffa4dbcf0c88f79 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 May 2024 17:06:42 +0100 Subject: [PATCH 02/29] solaris support start. --- src/tools/miri/README.md | 2 +- src/tools/miri/ci/ci.sh | 3 +-- src/tools/miri/src/shims/unix/mem.rs | 7 +++++-- .../src/shims/unix/solarish/foreign_items.rs | 19 ++++++++++++++++++- .../miri/tests/pass-dep/shims/libc-misc.rs | 4 ++-- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 5b3e2a588b4fa..d086987f437b6 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -227,7 +227,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. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 713c97830a5a2..d1dcacdfdf5d5 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -146,8 +146,7 @@ case $HOST_TARGET in 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=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random 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 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/tests/pass-dep/shims/libc-misc.rs b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs index 9644920c499c6..32898d4959351 100644 --- a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs @@ -136,7 +136,7 @@ fn test_dlsym() { assert_eq!(errno, libc::EBADF); } -#[cfg(not(any(target_os = "macos", target_os = "illumos")))] +#[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] fn test_reallocarray() { unsafe { let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); @@ -234,7 +234,7 @@ fn main() { test_strcpy(); test_memalign(); - #[cfg(not(any(target_os = "macos", target_os = "illumos")))] + #[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] test_reallocarray(); #[cfg(target_os = "linux")] From 37087dbed70f6390592ac78a4b766088f6caa571 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 22:34:53 +0200 Subject: [PATCH 03/29] reduce tokio features --- src/tools/miri/test_dependencies/Cargo.lock | 78 --------------------- src/tools/miri/test_dependencies/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 79 deletions(-) diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index 3dd5eb9b91c55..e11159f9e6063 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" @@ -233,29 +211,6 @@ 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" @@ -316,15 +271,6 @@ 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 +290,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 +330,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..f693aab242572 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -19,7 +19,7 @@ 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" ] } From 6a47bd4cf7cdc0323600d10cd162fefb1c108d69 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 22:36:00 +0200 Subject: [PATCH 04/29] remove rand test the actual target-specific things we want to test are all in getrandom, and rand already tests miri itself --- src/tools/miri/test_dependencies/Cargo.lock | 37 --------------------- src/tools/miri/test_dependencies/Cargo.toml | 1 - src/tools/miri/tests/pass-dep/rand.rs | 23 ------------- 3 files changed, 61 deletions(-) delete mode 100644 src/tools/miri/tests/pass-dep/rand.rs diff --git a/src/tools/miri/test_dependencies/Cargo.lock b/src/tools/miri/test_dependencies/Cargo.lock index e11159f9e6063..c73d13a4620c2 100644 --- a/src/tools/miri/test_dependencies/Cargo.lock +++ b/src/tools/miri/test_dependencies/Cargo.lock @@ -170,7 +170,6 @@ dependencies = [ "libc", "num_cpus", "page_size", - "rand", "tempfile", "tokio", "windows-sys 0.52.0", @@ -217,12 +216,6 @@ 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" @@ -241,36 +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 = "rustc-demangle" version = "0.1.23" diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml index f693aab242572..1894f53ce49c0 100644 --- a/src/tools/miri/test_dependencies/Cargo.toml +++ b/src/tools/miri/test_dependencies/Cargo.toml @@ -15,7 +15,6 @@ 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" 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::(); -} From a5baa150e3362ae83bda62dbbe73b0f5e2211364 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 22:38:19 +0200 Subject: [PATCH 05/29] getrandom: test with and without isolation also add some comments for why we keep certain old obscure APIs supported --- .../src/shims/unix/linux/foreign_items.rs | 33 +++++++------------ .../miri/src/shims/windows/foreign_items.rs | 3 ++ src/tools/miri/tests/pass-dep/getrandom.rs | 3 +- 3 files changed, 16 insertions(+), 23 deletions(-) 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/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index dba5b7a906f91..28dad974771d6 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -513,6 +513,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 +523,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 +532,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/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(); From 82e2144ed74648485dc27dbb67f594acd1b14916 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 May 2024 09:45:11 +0200 Subject: [PATCH 06/29] avoid code duplication between realloc and malloc --- src/tools/miri/src/shims/alloc.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 79531598c003a..61e639f76ed9a 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -124,13 +124,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let new_align = this.min_align(new_size, kind); 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, kind) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. From 43c99160d39eb3938a936d6a074b770cc13612af Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Tue, 16 Apr 2024 13:33:08 +0200 Subject: [PATCH 07/29] Implement wcslen --- src/tools/miri/src/helpers.rs | 43 ++++++++++++++++++----- src/tools/miri/src/shims/foreign_items.rs | 10 ++++++ src/tools/miri/tests/pass-dep/wcslen.rs | 20 +++++++++++ 3 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 src/tools/miri/tests/pass-dep/wcslen.rs diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 40c2008ac9435..795f21ddb9b13 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -982,29 +982,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 +1054,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/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index c51a27b7458a8..28028479ac3f2 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -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/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); +} From dd5437fdffdb2a34ef19879dabfd30ff980771c3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 May 2024 11:15:07 +0200 Subject: [PATCH 08/29] organize libc tests into a proper folder, and run some of them on Windows --- src/tools/miri/ci/ci.sh | 8 +- .../libc_pthread_cond_double_destroy.rs | 2 +- .../libc_pthread_cond_double_destroy.stderr | 0 .../libc_pthread_condattr_double_destroy.rs | 2 +- ...ibc_pthread_condattr_double_destroy.stderr | 0 .../libc_pthread_create_main_terminate.rs | 2 +- .../libc_pthread_create_too_few_args.rs | 2 +- .../libc_pthread_create_too_many_args.rs | 2 +- .../concurrency/libc_pthread_join_detached.rs | 2 +- .../concurrency/libc_pthread_join_joined.rs | 2 +- .../concurrency/libc_pthread_join_main.rs | 2 +- .../concurrency/libc_pthread_join_multiple.rs | 2 +- .../concurrency/libc_pthread_join_self.rs | 2 +- .../libc_pthread_mutex_NULL_deadlock.rs | 2 +- .../libc_pthread_mutex_NULL_deadlock.stderr | 0 .../libc_pthread_mutex_deadlock.rs | 2 +- .../libc_pthread_mutex_deadlock.stderr | 0 .../libc_pthread_mutex_default_deadlock.rs | 2 +- ...libc_pthread_mutex_default_deadlock.stderr | 0 .../libc_pthread_mutex_destroy_locked.rs | 2 +- .../libc_pthread_mutex_destroy_locked.stderr | 0 .../libc_pthread_mutex_double_destroy.rs | 2 +- .../libc_pthread_mutex_double_destroy.stderr | 0 .../libc_pthread_mutex_normal_deadlock.rs | 2 +- .../libc_pthread_mutex_normal_deadlock.stderr | 0 ...bc_pthread_mutex_normal_unlock_unlocked.rs | 2 +- ...thread_mutex_normal_unlock_unlocked.stderr | 0 .../libc_pthread_mutex_wrong_owner.rs | 2 +- .../libc_pthread_mutex_wrong_owner.stderr | 0 .../libc_pthread_mutexattr_double_destroy.rs | 2 +- ...bc_pthread_mutexattr_double_destroy.stderr | 0 ...libc_pthread_rwlock_destroy_read_locked.rs | 2 +- ..._pthread_rwlock_destroy_read_locked.stderr | 0 ...ibc_pthread_rwlock_destroy_write_locked.rs | 2 +- ...pthread_rwlock_destroy_write_locked.stderr | 0 .../libc_pthread_rwlock_double_destroy.rs | 2 +- .../libc_pthread_rwlock_double_destroy.stderr | 0 ...wlock_read_write_deadlock_single_thread.rs | 2 +- ...k_read_write_deadlock_single_thread.stderr | 0 .../libc_pthread_rwlock_read_wrong_owner.rs | 2 +- ...ibc_pthread_rwlock_read_wrong_owner.stderr | 0 .../libc_pthread_rwlock_unlock_unlocked.rs | 2 +- ...libc_pthread_rwlock_unlock_unlocked.stderr | 0 ...libc_pthread_rwlock_write_read_deadlock.rs | 2 +- ..._pthread_rwlock_write_read_deadlock.stderr | 0 ...wlock_write_read_deadlock_single_thread.rs | 2 +- ...k_write_read_deadlock_single_thread.stderr | 0 ...ibc_pthread_rwlock_write_write_deadlock.rs | 2 +- ...pthread_rwlock_write_write_deadlock.stderr | 0 ...lock_write_write_deadlock_single_thread.rs | 2 +- ..._write_write_deadlock_single_thread.stderr | 0 .../libc_pthread_rwlock_write_wrong_owner.rs | 2 +- ...bc_pthread_rwlock_write_wrong_owner.stderr | 0 .../{shims => libc}/env-set_var-data-race.rs | 2 +- .../env-set_var-data-race.stderr | 0 .../{shims => libc}/fs/close_stdout.rs | 2 +- .../{shims => libc}/fs/close_stdout.stderr | 0 .../{shims => libc}/fs/isolated_stdin.rs | 2 +- .../{shims => libc}/fs/isolated_stdin.stderr | 0 .../fs/mkstemp_immutable_arg.rs | 2 +- .../fs/mkstemp_immutable_arg.stderr | 0 .../{shims => libc}/fs/read_from_stdout.rs | 2 +- .../fs/read_from_stdout.stderr | 0 .../fs/unix_open_missing_required_mode.rs | 2 +- .../fs/unix_open_missing_required_mode.stderr | 0 .../{shims => libc}/fs/write_to_stdin.rs | 2 +- .../{shims => libc}/fs/write_to_stdin.stderr | 0 .../fail-dep/{shims => libc}/memchr_null.rs | 2 - .../{shims => libc}/memchr_null.stderr | 0 .../fail-dep/{shims => libc}/memcmp_null.rs | 2 - .../{shims => libc}/memcmp_null.stderr | 0 .../fail-dep/{shims => libc}/memcmp_zero.rs | 1 - .../{shims => libc}/memcmp_zero.stderr | 0 .../fail-dep/{shims => libc}/memcpy_zero.rs | 1 - .../{shims => libc}/memcpy_zero.stderr | 0 .../fail-dep/{shims => libc}/memrchr_null.rs | 2 +- .../{shims => libc}/memrchr_null.stderr | 0 .../{shims => libc}/mmap_invalid_dealloc.rs | 2 +- .../mmap_invalid_dealloc.stderr | 0 .../{shims => libc}/mmap_use_after_munmap.rs | 2 +- .../mmap_use_after_munmap.stderr | 0 .../{shims => libc}/munmap_partial.rs | 2 +- .../{shims => libc}/munmap_partial.stderr | 0 .../tests/fail-dep/{ => libc}/realloc-zero.rs | 2 - .../fail-dep/{ => libc}/realloc-zero.stderr | 0 .../unsupported_incomplete_function.rs | 2 +- .../unsupported_incomplete_function.stderr | 0 .../miri/tests/panic/unsupported_syscall.rs | 2 +- src/tools/miri/tests/pass-dep/calloc.rs | 22 --- .../env-cleanup-data-race.rs | 2 +- .../libc_pthread_cond_timedwait.rs | 2 +- .../libc_pthread_cond_timedwait_isolated.rs | 2 +- .../concurrency/tls_pthread_drop_order.rs | 2 +- .../miri/tests/pass-dep/extra_fn_ptr_gc.rs | 2 +- .../fcntl_f-fullfsync_apple.rs | 0 .../fcntl_f-fullfsync_apple.stderr | 0 .../{shims => libc}/libc-fs-with-isolation.rs | 0 .../libc-fs-with-isolation.stderr | 0 .../tests/pass-dep/{shims => libc}/libc-fs.rs | 0 .../pass-dep/{shims => libc}/libc-fs.stderr | 0 .../pass-dep/{shims => libc}/libc-fs.stdout | 0 .../{shims/libc-misc.rs => libc/libc-mem.rs} | 163 ++++++++++-------- .../miri/tests/pass-dep/libc/libc-misc.rs | 68 ++++++++ .../pass-dep/{shims => libc}/libc-random.rs | 0 .../pass-dep/{shims => libc}/libc-time.rs | 0 .../tests/pass-dep/{shims => libc}/mmap.rs | 2 +- .../pass-dep/{shims => libc}/pthread-sync.rs | 2 +- .../{shims => libc}/pthread-threadname.rs | 2 +- src/tools/miri/tests/pass-dep/malloc.rs | 48 ------ 109 files changed, 211 insertions(+), 208 deletions(-) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_cond_double_destroy.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_cond_double_destroy.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_condattr_double_destroy.rs (92%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_condattr_double_destroy.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_NULL_deadlock.rs (90%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_NULL_deadlock.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_deadlock.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_deadlock.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_default_deadlock.rs (91%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_default_deadlock.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_destroy_locked.rs (92%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_destroy_locked.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_double_destroy.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_double_destroy.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_deadlock.rs (92%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_deadlock.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_unlock_unlocked.rs (92%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_unlock_unlocked.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_wrong_owner.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_wrong_owner.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutexattr_double_destroy.rs (91%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutexattr_double_destroy.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_read_locked.rs (83%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_read_locked.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_write_locked.rs (83%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_write_locked.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_double_destroy.rs (89%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_double_destroy.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_write_deadlock_single_thread.rs (82%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_wrong_owner.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_wrong_owner.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_unlock_unlocked.rs (78%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_unlock_unlocked.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock_single_thread.rs (82%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock_single_thread.rs (82%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_wrong_owner.rs (93%) rename src/tools/miri/tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_wrong_owner.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/env-set_var-data-race.rs (90%) rename src/tools/miri/tests/fail-dep/{shims => libc}/env-set_var-data-race.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/close_stdout.rs (82%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/close_stdout.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/isolated_stdin.rs (83%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/isolated_stdin.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/mkstemp_immutable_arg.rs (86%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/mkstemp_immutable_arg.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/read_from_stdout.rs (83%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/read_from_stdout.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/unix_open_missing_required_mode.rs (89%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/unix_open_missing_required_mode.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/write_to_stdin.rs (80%) rename src/tools/miri/tests/fail-dep/{shims => libc}/fs/write_to_stdin.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memchr_null.rs (77%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memchr_null.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memcmp_null.rs (78%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memcmp_null.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memcmp_zero.rs (90%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memcmp_zero.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memcpy_zero.rs (88%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memcpy_zero.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memrchr_null.rs (81%) rename src/tools/miri/tests/fail-dep/{shims => libc}/memrchr_null.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/mmap_invalid_dealloc.rs (90%) rename src/tools/miri/tests/fail-dep/{shims => libc}/mmap_invalid_dealloc.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/mmap_use_after_munmap.rs (90%) rename src/tools/miri/tests/fail-dep/{shims => libc}/mmap_use_after_munmap.stderr (100%) rename src/tools/miri/tests/fail-dep/{shims => libc}/munmap_partial.rs (94%) rename src/tools/miri/tests/fail-dep/{shims => libc}/munmap_partial.stderr (100%) rename src/tools/miri/tests/fail-dep/{ => libc}/realloc-zero.rs (81%) rename src/tools/miri/tests/fail-dep/{ => libc}/realloc-zero.stderr (100%) rename src/tools/miri/tests/fail-dep/{ => libc}/unsupported_incomplete_function.rs (87%) rename src/tools/miri/tests/fail-dep/{ => libc}/unsupported_incomplete_function.stderr (100%) delete mode 100644 src/tools/miri/tests/pass-dep/calloc.rs rename src/tools/miri/tests/pass-dep/{shims => concurrency}/env-cleanup-data-race.rs (93%) rename src/tools/miri/tests/pass-dep/{shims => libc}/fcntl_f-fullfsync_apple.rs (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/fcntl_f-fullfsync_apple.stderr (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-fs-with-isolation.rs (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-fs-with-isolation.stderr (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-fs.rs (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-fs.stderr (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-fs.stdout (100%) rename src/tools/miri/tests/pass-dep/{shims/libc-misc.rs => libc/libc-mem.rs} (72%) create mode 100644 src/tools/miri/tests/pass-dep/libc/libc-misc.rs rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-random.rs (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/libc-time.rs (100%) rename src/tools/miri/tests/pass-dep/{shims => libc}/mmap.rs (99%) rename src/tools/miri/tests/pass-dep/{shims => libc}/pthread-sync.rs (99%) rename src/tools/miri/tests/pass-dep/{shims => libc}/pthread-threadname.rs (97%) delete mode 100644 src/tools/miri/tests/pass-dep/malloc.rs diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index d1dcacdfdf5d5..c28a6f183b36f 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -143,10 +143,10 @@ case $HOST_TARGET in # 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 - MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random + MIRI_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 + MIRI_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 + MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random 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 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/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/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/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/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs similarity index 100% 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 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 100% rename from src/tools/miri/tests/pass-dep/shims/libc-fs.rs rename to src/tools/miri/tests/pass-dep/libc/libc-fs.rs 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 72% 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 32898d4959351..33cdc18b70f9f 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,71 @@ 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 + 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); + } } -#[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] -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()); + + 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); } } +#[cfg(not(target_os = "windows"))] fn test_memalign() { // A normal allocation. unsafe { @@ -225,18 +219,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", target_os = "solaris")))] + #[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 100% rename from src/tools/miri/tests/pass-dep/shims/libc-time.rs rename to src/tools/miri/tests/pass-dep/libc/libc-time.rs 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); - } -} From d2472ba2cb52ff18eea38b1839e01cdb961640ee Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 7 May 2024 20:18:39 +0200 Subject: [PATCH 09/29] README: update introduction --- src/tools/miri/README.md | 70 ++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index d086987f437b6..3b2f3ea34f4ea 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 From 34f64cddf54595bd3a64418b038ce0d593458b30 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 7 May 2024 20:33:18 +0200 Subject: [PATCH 10/29] remove problems that I do not think we have seen in a while --- src/tools/miri/README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 3b2f3ea34f4ea..2c76749fbca13 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -265,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 From 1601b27d1ce29b29a011cd036c5e10e120ec89f1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 May 2024 18:01:21 +0200 Subject: [PATCH 11/29] io::Error handling: keep around the full io::Error for longer so we can give better errors --- src/tools/miri/src/helpers.rs | 19 +++++++-------- src/tools/miri/src/shims/unix/env.rs | 8 +++---- src/tools/miri/src/shims/unix/fd.rs | 4 ++-- src/tools/miri/src/shims/unix/fs.rs | 24 +++++++++---------- src/tools/miri/src/shims/windows/env.rs | 8 +++---- .../miri/src/shims/windows/foreign_items.rs | 2 +- 6 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 795f21ddb9b13..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()) } } 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/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 57ae209416c86..eb241556a5691 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -378,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); } @@ -434,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); } @@ -465,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); } @@ -766,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); } @@ -794,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); } @@ -822,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); } @@ -859,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)) } } @@ -947,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 } }; @@ -1299,7 +1299,7 @@ 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) } } @@ -1382,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)) } } @@ -1503,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); } }, @@ -1582,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/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 28dad974771d6..44b0f25b55c0f 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -225,7 +225,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) => { From 4ab79e56222de65e02aa5b43a6bb9779895f6104 Mon Sep 17 00:00:00 2001 From: tiif Date: Thu, 9 May 2024 04:34:32 +0800 Subject: [PATCH 12/29] Implement non-null pointer for malloc(0) --- src/tools/miri/src/shims/alloc.rs | 27 +++++++++---------- .../fail-dep/libc/malloc_zero_double_free.rs | 7 +++++ .../libc/malloc_zero_double_free.stderr | 25 +++++++++++++++++ .../fail-dep/libc/malloc_zero_memory_leak.rs | 5 ++++ .../libc/malloc_zero_memory_leak.stderr | 15 +++++++++++ .../miri/tests/pass-dep/libc/libc-mem.rs | 14 ++++++---- 6 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_double_free.stderr create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/malloc_zero_memory_leak.stderr diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 61e639f76ed9a..4eefb8b439b40 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -36,6 +36,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if kind == MiriMemoryKind::WinHeap || size >= min_align { return Align::from_bytes(min_align).unwrap(); } + 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 { let next_pow2 = x.next_power_of_two(); @@ -85,21 +88,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { 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.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()) } fn free( 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/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs index 33cdc18b70f9f..5df3ace7496fd 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs @@ -110,9 +110,10 @@ fn test_malloc() { } unsafe { - // Realloc with size 0 is okay for the null pointer + // 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()); + assert!(!p2.is_null()); + libc::free(p2); } unsafe { @@ -126,13 +127,16 @@ fn test_malloc() { fn test_calloc() { unsafe { let p1 = libc::calloc(0, 0); - assert!(p1.is_null()); + assert!(!p1.is_null()); + libc::free(p1); let p2 = libc::calloc(20, 0); - assert!(p2.is_null()); + assert!(!p2.is_null()); + libc::free(p2); let p3 = libc::calloc(0, 20); - assert!(p3.is_null()); + assert!(!p3.is_null()); + libc::free(p3); let p4 = libc::calloc(4, 8); assert!(!p4.is_null()); From 6580a22726abf9544c0752a3cd26860643b8aa5d Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Wed, 8 May 2024 14:43:33 -0400 Subject: [PATCH 13/29] Allow test targets to be set via CLI args --- src/tools/miri/miri-script/src/commands.rs | 57 +++++++++++++++------- src/tools/miri/miri-script/src/main.rs | 41 +++++++++++++--- 2 files changed, 75 insertions(+), 23 deletions(-) diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 6d07455c0deb6..968dc62cd9896 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -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<&str>) -> 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,27 @@ 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!["--target", target] } else { vec![] }; + 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})"); + } 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,7 +164,7 @@ 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), @@ -446,16 +449,23 @@ impl Command { Ok(()) } - fn test(bless: bool, flags: Vec) -> Result<()> { + fn test(bless: bool, flags: Vec, target: Option) -> Result<()> { let mut e = MiriEnv::new()?; + + if let Some(target) = target.as_deref() { + // Tell the sysroot which target to test. + e.sh.set_var("MIRI_TEST_TARGET", target); + } + // 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. if bless { e.sh.set_var("RUSTC_BLESS", "Gesundheit"); } + e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?; Ok(()) } @@ -476,14 +486,26 @@ impl Command { .take_while(|arg| *arg != "--") .tuple_windows() .find(|(first, _)| *first == "--target"); - if let Some((_, target)) = target { + + let target_triple = if let Some((_, target)) = target { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); + + let triple = target + .clone() + .into_string() + .map_err(|_| anyhow!("invalid target triple encoding"))?; + Some(triple) } 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()); - } + flags.push(target.clone().into()); + + Some(target) + } else { + None + }; + // 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"); @@ -492,7 +514,8 @@ impl Command { } // 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_triple.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..c92513a0fa796 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -33,6 +33,9 @@ pub enum Command { bless: bool, /// Flags that are passed through to `cargo test`. flags: Vec, + /// The cross-interpretation target. + /// If none then the host is the target. + target: Option, }, /// Build miri, set up a sysroot and then run the driver with the given . /// (Also respects MIRIFLAGS environment variable.) @@ -84,9 +87,9 @@ Just build miri. are passed to `cargo build`. ./miri check : Just check miri. are passed to `cargo check`. -./miri test [--bless] : +./miri test [--bless] [--target] : Build miri, set up a sysroot and then run the test suite. are passed -to the final `cargo test` invocation. +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 . @@ -147,12 +150,38 @@ 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 = std::env::var("MIRI_TEST_TARGET").ok(); + 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(); + + // Check that there is a target triple, and that it is unicode. + target = if let Some(value) = args.peek() { + let target_str = value + .clone() + .into_string() + .map_err(|_| anyhow!("invalid target triple encoding"))?; + Some(target_str) + } else { + bail!("no target triple found") + } + } + // Only parse the leading flags. + _ => break, + } + + // Consume the flag, look at the next one. args.next().unwrap(); } - Command::Test { bless, flags: args.collect() } + + // Prepend a "--" so that the rest of the arguments are passed to the test driver. + let args = std::iter::once(OsString::from("--")).chain(args); + Command::Test { bless, flags: args.collect(), target } } Some("run") => { let mut dep = false; From 6e564ed9fdce38a56efc3feeaccf171bc91d92c9 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Wed, 8 May 2024 14:44:06 -0400 Subject: [PATCH 14/29] Update CI script for the miri-script test changes --- src/tools/miri/ci/ci.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index c28a6f183b36f..18f1bf18c7d23 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -59,7 +59,7 @@ 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 tests/{pass,panic} fi if [ -n "${MANY_SEEDS-}" ]; then # Also run some many-seeds tests. @@ -107,7 +107,7 @@ function run_tests_minimal { exit 1 fi - time ./miri test -- "$@" + time ./miri test "$@" # 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} From 620bf348e1d793e2b1e13ea0b4af52f883d911ae Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Wed, 8 May 2024 16:37:48 -0400 Subject: [PATCH 15/29] Update documentation for miri-script test changes --- src/tools/miri/CONTRIBUTING.md | 9 ++++----- src/tools/miri/README.md | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 60bc1d5282dd6..39aed51f5df9c 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -72,14 +72,13 @@ For example: You can (cross-)run the entire test suite using: -``` -./miri test -MIRI_TEST_TARGET=i686-unknown-linux-gnu ./miri test +```sh +./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`. #### Fine grained logging diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 2c76749fbca13..6e0c96499e206 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -464,7 +464,7 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): 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 + architecture to test against. The `--target` flag may be used 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. From d43cb7121e8d651e9e2d4f48ead98c173f26af7d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 09:18:14 +0200 Subject: [PATCH 16/29] minor tweaks --- src/tools/miri/CONTRIBUTING.md | 1 + src/tools/miri/miri-script/src/commands.rs | 6 ++---- src/tools/miri/miri-script/src/main.rs | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 39aed51f5df9c..57682e60c370f 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -73,6 +73,7 @@ For example: You can (cross-)run the entire test suite using: ```sh +./miri test ./miri test --target i686-unknown-linux-gnu ``` diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 968dc62cd9896..43abf180d3e75 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -491,10 +491,8 @@ impl Command { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); - let triple = target - .clone() - .into_string() - .map_err(|_| anyhow!("invalid target triple encoding"))?; + let triple = + target.clone().into_string().map_err(|_| anyhow!("target triple is not UTF-8"))?; Some(triple) } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { // Convert `MIRI_TEST_TARGET` into `--target`. diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index c92513a0fa796..c19c4c91c6519 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -87,7 +87,7 @@ Just build miri. are passed to `cargo build`. ./miri check : Just check miri. are passed to `cargo check`. -./miri test [--bless] [--target] : +./miri test [--bless] [--target ] : Build miri, set up a sysroot and then run the test suite. are passed to the test harness. @@ -165,7 +165,7 @@ fn main() -> Result<()> { let target_str = value .clone() .into_string() - .map_err(|_| anyhow!("invalid target triple encoding"))?; + .map_err(|_| anyhow!("target triple is not UTF-8"))?; Some(target_str) } else { bail!("no target triple found") From e16f46cdab0c853a9d1fab15e4746947de72a202 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 11:13:54 +0200 Subject: [PATCH 17/29] make MIRI_TEST_TARGET entirely an internal thing --- src/tools/miri/CONTRIBUTING.md | 45 +++++++++++-- src/tools/miri/README.md | 34 ---------- src/tools/miri/ci/ci.sh | 67 ++++++++++--------- src/tools/miri/miri-script/src/commands.rs | 77 +++++++++------------- src/tools/miri/miri-script/src/main.rs | 57 ++++++++++------ src/tools/miri/miri-script/src/util.rs | 24 +++++++ src/tools/miri/test-cargo-miri/run-test.py | 14 ++-- 7 files changed, 175 insertions(+), 143 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 57682e60c370f..5a747e7935e34 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -78,8 +78,8 @@ You can (cross-)run the entire test suite using: ``` `./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). Multiple filters -are supported: `./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,39 @@ 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. diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 6e0c96499e206..1bdfc4ac1a78f 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -463,9 +463,6 @@ 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. The `--target` flag may be used 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 @@ -476,37 +473,6 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `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 18f1bf18c7d23..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,33 +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-mem 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-mem 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-mem libc-misc libc-random - MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random - 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 @@ -162,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 43abf180d3e75..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; @@ -25,7 +25,7 @@ impl MiriEnv { /// Returns the location of the sysroot. /// /// If the target is None the sysroot will be built for the host machine. - fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&str>) -> Result { + 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()); @@ -38,11 +38,12 @@ impl MiriEnv { self.build(&manifest_path, &[], quiet)?; let target_flag = - &if let Some(target) = target { vec!["--target", target] } else { vec![] }; + if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] }; + let target_flag = &target_flag; if !quiet { if let Some(target) = target { - eprintln!("$ (building Miri sysroot for {target})"); + eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy()); } else { eprintln!("$ (building Miri sysroot)"); } @@ -170,7 +171,7 @@ impl Command { 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), @@ -372,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"); @@ -380,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![])?; @@ -397,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"); @@ -404,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()?; } @@ -449,23 +456,26 @@ impl Command { Ok(()) } - fn test(bless: bool, flags: Vec, target: Option) -> Result<()> { + fn test(bless: bool, mut flags: Vec, target: Option) -> Result<()> { let mut e = MiriEnv::new()?; - if let Some(target) = target.as_deref() { - // Tell the sysroot which target to test. - e.sh.set_var("MIRI_TEST_TARGET", target); - } - // Prepare a sysroot. 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(()) } @@ -477,43 +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"); - - let target_triple = if let Some((_, target)) = target { - // Found it! - e.sh.set_var("MIRI_TEST_TARGET", target); - - let triple = - target.clone().into_string().map_err(|_| anyhow!("target triple is not UTF-8"))?; - Some(triple) - } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { - // Convert `MIRI_TEST_TARGET` into `--target`. - flags.push("--target".into()); - flags.push(target.clone().into()); - - Some(target) - } else { - None - }; + 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, target_triple.as_deref())?; + 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 c19c4c91c6519..a8626ceb45d75 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -31,11 +31,11 @@ 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`. - flags: Vec, /// The cross-interpretation target. /// If none then the host is the target. - target: Option, + 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 . /// (Also respects MIRIFLAGS environment variable.) @@ -61,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, }, @@ -88,8 +89,8 @@ Just build miri. are passed to `cargo build`. Just check miri. are passed to `cargo check`. ./miri test [--bless] [--target ] : -Build miri, set up a sysroot and then run the test suite. are passed -to the test harness. +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 . @@ -113,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. @@ -150,7 +151,7 @@ fn main() -> Result<()> { Some("build") => Command::Build { flags: args.collect() }, Some("check") => Command::Check { flags: args.collect() }, Some("test") => { - let mut target = std::env::var("MIRI_TEST_TARGET").ok(); + let mut target = None; let mut bless = false; while let Some(arg) = args.peek().and_then(|s| s.to_str()) { @@ -159,17 +160,11 @@ fn main() -> Result<()> { "--target" => { // Skip "--target" args.next().unwrap(); - - // Check that there is a target triple, and that it is unicode. - target = if let Some(value) = args.peek() { - let target_str = value - .clone() - .into_string() - .map_err(|_| anyhow!("target triple is not UTF-8"))?; - Some(target_str) - } else { - bail!("no target triple found") - } + // 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, @@ -179,8 +174,6 @@ fn main() -> Result<()> { args.next().unwrap(); } - // Prepend a "--" so that the rest of the arguments are passed to the test driver. - let args = std::iter::once(OsString::from("--")).chain(args); Command::Test { bless, flags: args.collect(), target } } Some("run") => { @@ -217,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/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 2639d29b73de6..8bab66f3af9d8 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): @@ -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,21 @@ 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 = 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"]: From cb448439006c2cd068b4ab667175fdcbe929f0ec Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 11:21:10 +0200 Subject: [PATCH 18/29] make RUSTC_BLESS entirely an internal thing --- src/tools/miri/CONTRIBUTING.md | 2 ++ src/tools/miri/README.md | 2 -- src/tools/miri/test-cargo-miri/run-test.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 5a747e7935e34..092ad46a7cad3 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -322,3 +322,5 @@ binaries, and as such worth documenting: 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 1bdfc4ac1a78f..92cb7b37820b9 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -468,8 +468,6 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `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. diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 8bab66f3af9d8..83f3e4c919b9a 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -36,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 @@ -185,6 +185,7 @@ def test_cargo_miri_test(): 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__))) From 1edd3d59b0233249db0fdc3452aa8ba02ab95f22 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 11:42:05 +0200 Subject: [PATCH 19/29] do not run symlink tests on Windows hosts --- .../tests/pass-dep/libc/libc-fs-readlink.rs | 51 +++++++++++ .../pass-dep/libc/libc-fs-with-isolation.rs | 2 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 84 ++---------------- .../miri/tests/pass-dep/libc/libc-time.rs | 2 +- src/tools/miri/tests/pass/shims/fs-symlink.rs | 50 +++++++++++ src/tools/miri/tests/pass/shims/fs.rs | 88 ++++--------------- src/tools/miri/tests/utils/fs.rs | 24 +++++ 7 files changed, 151 insertions(+), 150 deletions(-) create mode 100644 src/tools/miri/tests/pass-dep/libc/libc-fs-readlink.rs create mode 100644 src/tools/miri/tests/pass/shims/fs-symlink.rs 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/libc/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs index 5185db0b0e29f..088a632427e70 100644 --- a/src/tools/miri/tests/pass-dep/libc/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/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 0dd849a045dfa..80c9757e9c956 100644 --- a/src/tools/miri/tests/pass-dep/libc/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/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index 69c75bd8caf3c..ea1e49a072571 100644 --- a/src/tools/miri/tests/pass-dep/libc/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/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/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 +} From c21c5baad9e8404777297705b6e55c0abcc7e6d9 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Thu, 9 May 2024 16:58:25 -0700 Subject: [PATCH 20/29] Document proper usage of `fmt::Error` and `fmt()`'s `Result`. Documentation of these properties previously existed in a lone paragraph in the `fmt` module's documentation: However, users looking to implement a formatting trait won't necessarily look there. Therefore, let's add the critical information (that formatting per se is infallible) to all the involved items. --- library/alloc/src/fmt.rs | 2 +- library/core/src/fmt/fmt_trait_method_doc.md | 8 ++++ library/core/src/fmt/mod.rs | 42 +++++++++++++------- 3 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 library/core/src/fmt/fmt_trait_method_doc.md diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index b9918752540f3..d7dc6ae6694b5 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -403,7 +403,7 @@ //! is, a formatting implementation must and may only return an error if the //! passed-in [`Formatter`] returns an error. This is because, contrary to what //! the function signature might suggest, string formatting is an infallible -//! operation. This function only returns a result because writing to the +//! operation. This function only returns a [`Result`] because writing to the //! underlying stream might fail and it must provide a way to propagate the fact //! that an error has occurred back up the stack. //! diff --git a/library/core/src/fmt/fmt_trait_method_doc.md b/library/core/src/fmt/fmt_trait_method_doc.md new file mode 100644 index 0000000000000..493d929243d2d --- /dev/null +++ b/library/core/src/fmt/fmt_trait_method_doc.md @@ -0,0 +1,8 @@ +Formats the value using the given formatter. + +# Errors + +This function should return [`Err`] if, and only if, the provided [`Formatter`] returns [`Err`]. +String formatting is considered an infallible operation; this function only +returns a [`Result`] because writing to the underlying stream might fail and it must +provide a way to propagate the fact that an error has occurred back up the stack. diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index ce0643a3f5ef5..9b372eac52455 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -72,14 +72,24 @@ pub type Result = result::Result<(), Error>; /// The error type which is returned from formatting a message into a stream. /// /// This type does not support transmission of an error other than that an error -/// occurred. Any extra information must be arranged to be transmitted through -/// some other means. -/// -/// An important thing to remember is that the type `fmt::Error` should not be +/// occurred. This is because, despite the existence of this error, +/// string formatting is considered an infallible operation. +/// `fmt()` implementors should not return this `Error` unless they received it from their +/// [`Formatter`]. The only time your code should create a new instance of this +/// error is when implementing `fmt::Write`, in order to cancel the formatting operation when +/// writing to the underlying stream fails. +/// +/// Any extra information must be arranged to be transmitted through some other means, +/// such as storing it in a field to be consulted after the formatting operation has been +/// cancelled. (For example, this is how [`std::io::Write::write_fmt()`] propagates IO errors +/// during writing.) +/// +/// This type, `fmt::Error`, should not be /// confused with [`std::io::Error`] or [`std::error::Error`], which you may also /// have in scope. /// /// [`std::io::Error`]: ../../std/io/struct.Error.html +/// [`std::io::Write::write_fmt()`]: ../../std/io/trait.Write.html#method.write_fmt /// [`std::error::Error`]: ../../std/error/trait.Error.html /// /// # Examples @@ -118,8 +128,10 @@ pub trait Write { /// This function will return an instance of [`std::fmt::Error`][Error] on error. /// /// The purpose of that error is to abort the formatting operation when the underlying - /// destination encounters some error preventing it from accepting more text; it should - /// generally be propagated rather than handled, at least when implementing formatting traits. + /// destination encounters some error preventing it from accepting more text; + /// in particular, it does not communicate any information about *what* error occurred. + /// It should generally be propagated rather than handled, at least when implementing + /// formatting traits. /// /// # Examples /// @@ -586,7 +598,7 @@ impl Display for Arguments<'_> { #[rustc_diagnostic_item = "Debug"] #[rustc_trivial_field_reads] pub trait Debug { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] /// /// # Examples /// @@ -703,7 +715,7 @@ pub use macros::Debug; #[rustc_diagnostic_item = "Display"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Display { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] /// /// # Examples /// @@ -777,7 +789,7 @@ pub trait Display { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait Octal { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -836,7 +848,7 @@ pub trait Octal { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait Binary { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -891,7 +903,7 @@ pub trait Binary { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait LowerHex { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -946,7 +958,7 @@ pub trait LowerHex { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait UpperHex { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -997,7 +1009,7 @@ pub trait UpperHex { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Pointer"] pub trait Pointer { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -1048,7 +1060,7 @@ pub trait Pointer { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait LowerExp { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -1099,7 +1111,7 @@ pub trait LowerExp { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub trait UpperExp { - /// Formats the value using the given formatter. + #[doc = include_str!("fmt_trait_method_doc.md")] #[stable(feature = "rust1", since = "1.0.0")] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } From c9fb74e08eeadcaa67f60bb7b07120a1e6c5f412 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Fri, 10 May 2024 16:06:38 +0300 Subject: [PATCH 21/29] check if `x test tests` missing any test directory Signed-off-by: onur-ozkan --- src/bootstrap/src/core/builder/tests.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 9898d495c023d..9710365ef114d 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -128,6 +128,26 @@ fn validate_path_remap() { }); } +#[test] +fn check_missing_paths_for_x_test_tests() { + let build = Build::new(configure("test", &["A-A"], &["A-A"])); + + let (_, tests_remap_paths) = + PATH_REMAP.iter().find(|(target_path, _)| *target_path == "tests").unwrap(); + + let tests_dir = fs::read_dir(build.src.join("tests")).unwrap(); + for dir in tests_dir { + let path = dir.unwrap().path(); + + // Skip if not a test directory. + if path.ends_with("tests/auxiliary") || !path.is_dir() { + continue + } + + assert!(tests_remap_paths.iter().any(|item| path.ends_with(*item)), "{} is missing in PATH_REMAP tests list.", path.display()); + } +} + #[test] fn test_exclude() { let mut config = configure("test", &["A-A"], &["A-A"]); From 569e547f189ed0aae49ef1902f6a485b0aa4275f Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Fri, 10 May 2024 16:06:56 +0300 Subject: [PATCH 22/29] remap missing path `tests/crashes` to `tests` Signed-off-by: onur-ozkan --- src/bootstrap/src/core/builder.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 8d7e53d2440bc..1d5dc0dddc063 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -322,6 +322,7 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ "tests/codegen-units", "tests/coverage", "tests/coverage-run-rustdoc", + "tests/crashes", "tests/debuginfo", "tests/incremental", "tests/mir-opt", From 0a0b40a9e001ce67f7bfbb7b050cd55223023d87 Mon Sep 17 00:00:00 2001 From: onur-ozkan Date: Fri, 10 May 2024 16:16:05 +0300 Subject: [PATCH 23/29] add "tidy-alphabetical" check on "tests" remap list Signed-off-by: onur-ozkan --- src/bootstrap/src/core/builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 1d5dc0dddc063..feffa89e8a579 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -317,6 +317,7 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ ( "tests", &[ + // tidy-alphabetical-start "tests/assembly", "tests/codegen", "tests/codegen-units", @@ -338,6 +339,7 @@ const PATH_REMAP: &[(&str, &[&str])] = &[ "tests/rustdoc-ui", "tests/ui", "tests/ui-fulldeps", + // tidy-alphabetical-end ], ), ]; From 25a3b6687ed2d5f7af165b838b57b28e0266b5e5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 May 2024 18:01:36 +0200 Subject: [PATCH 24/29] rename 'extern-so' to 'native-lib' --- src/tools/miri/.gitignore | 2 +- src/tools/miri/README.md | 12 +- src/tools/miri/src/bin/miri.rs | 13 +- src/tools/miri/src/eval.rs | 6 +- src/tools/miri/src/machine.rs | 12 +- src/tools/miri/src/shims/ffi_support.rs | 289 ------------------ src/tools/miri/src/shims/foreign_items.rs | 6 +- src/tools/miri/src/shims/mod.rs | 4 +- src/tools/miri/src/shims/native_lib.rs | 242 +++++++++++++++ .../fail/function_not_in_so.rs | 0 .../fail/function_not_in_so.stderr | 0 .../{extern-so => native-lib}/libtest.map | 0 .../pass/call_extern_c_fn.rs | 0 .../pass/call_extern_c_fn.stdout | 0 .../tests/{extern-so => native-lib}/test.c | 0 src/tools/miri/tests/ui.rs | 30 +- 16 files changed, 283 insertions(+), 333 deletions(-) delete mode 100644 src/tools/miri/src/shims/ffi_support.rs create mode 100644 src/tools/miri/src/shims/native_lib.rs rename src/tools/miri/tests/{extern-so => native-lib}/fail/function_not_in_so.rs (100%) rename src/tools/miri/tests/{extern-so => native-lib}/fail/function_not_in_so.stderr (100%) rename src/tools/miri/tests/{extern-so => native-lib}/libtest.map (100%) rename src/tools/miri/tests/{extern-so => native-lib}/pass/call_extern_c_fn.rs (100%) rename src/tools/miri/tests/{extern-so => native-lib}/pass/call_extern_c_fn.stdout (100%) rename src/tools/miri/tests/{extern-so => native-lib}/test.c (100%) 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/README.md b/src/tools/miri/README.md index 92cb7b37820b9..208a8b9ee617d 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -374,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 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/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/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 28028479ac3f2..1185d440ec681 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); } } 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/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/ui.rs b/src/tools/miri/tests/ui.rs index efeefbe29fbce..1a7c472450023 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(), From 2bb25d3f4ac8796a50f45409f3ef461ce83295f3 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Fri, 10 May 2024 12:27:49 -0400 Subject: [PATCH 25/29] Handle Deref expressions in invalid_reference_casting --- compiler/rustc_lint/src/reference_casting.rs | 6 ++++-- tests/ui/lint/reference_casting.rs | 7 +++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index b80e90c25a334..34153e3a220d3 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -202,8 +202,10 @@ fn is_cast_to_bigger_memory_layout<'tcx>( // if the current expr looks like this `&mut expr[index]` then just looking // at `expr[index]` won't give us the underlying allocation, so we just skip it - // the same logic applies field access like `&mut expr.field` - if let ExprKind::Index(..) | ExprKind::Field(..) = e_alloc.kind { + // the same logic applies field access `&mut expr.field` and reborrows `&mut *expr`. + if let ExprKind::Index(..) | ExprKind::Field(..) | ExprKind::Unary(UnOp::Deref, ..) = + e_alloc.kind + { return None; } diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs index 87a682249b00f..87fa42f94775e 100644 --- a/tests/ui/lint/reference_casting.rs +++ b/tests/ui/lint/reference_casting.rs @@ -261,6 +261,13 @@ unsafe fn bigger_layout() { let ptr = r as *mut i32 as *mut Vec3; unsafe { *ptr = Vec3(0, 0, 0) } } + + unsafe fn deref(v: &mut Vec3) { + let r = &mut v.0; + let r = &mut *r; + let ptr = &mut *(r as *mut i32 as *mut Vec3); + unsafe { *ptr = Vec3(0, 0, 0) } + } } const RAW_PTR: *mut u8 = 1 as *mut u8; From 9fb49faf4012ed677b9cb41f5d9618c8ce2585a8 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Wed, 7 Feb 2024 11:19:01 -0500 Subject: [PATCH 26/29] Stabilize `byte_slice_trim_ascii` for `&[u8]`/`&str` Remove feature from documentation examples Add rustc_const_stable attribute to stabilized functions Update intra-doc link for `u8::is_ascii_whitespace` on `&[u8]` functions --- library/core/src/slice/ascii.rs | 21 +++++++++------------ library/core/src/str/mod.rs | 15 ++++++--------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index 5c4f0bf9b2b49..8ad045275adec 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -114,18 +114,17 @@ impl [u8] { /// Returns a byte slice with leading ASCII whitespace bytes removed. /// /// 'Whitespace' refers to the definition used by - /// `u8::is_ascii_whitespace`. + /// [`u8::is_ascii_whitespace`]. /// /// # Examples /// /// ``` - /// #![feature(byte_slice_trim_ascii)] - /// /// assert_eq!(b" \t hello world\n".trim_ascii_start(), b"hello world\n"); /// assert_eq!(b" ".trim_ascii_start(), b""); /// assert_eq!(b"".trim_ascii_start(), b""); /// ``` - #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] + #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn trim_ascii_start(&self) -> &[u8] { let mut bytes = self; @@ -144,18 +143,17 @@ impl [u8] { /// Returns a byte slice with trailing ASCII whitespace bytes removed. /// /// 'Whitespace' refers to the definition used by - /// `u8::is_ascii_whitespace`. + /// [`u8::is_ascii_whitespace`]. /// /// # Examples /// /// ``` - /// #![feature(byte_slice_trim_ascii)] - /// /// assert_eq!(b"\r hello world\n ".trim_ascii_end(), b"\r hello world"); /// assert_eq!(b" ".trim_ascii_end(), b""); /// assert_eq!(b"".trim_ascii_end(), b""); /// ``` - #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] + #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn trim_ascii_end(&self) -> &[u8] { let mut bytes = self; @@ -175,18 +173,17 @@ impl [u8] { /// removed. /// /// 'Whitespace' refers to the definition used by - /// `u8::is_ascii_whitespace`. + /// [`u8::is_ascii_whitespace`]. /// /// # Examples /// /// ``` - /// #![feature(byte_slice_trim_ascii)] - /// /// assert_eq!(b"\r hello world\n ".trim_ascii(), b"hello world"); /// assert_eq!(b" ".trim_ascii(), b""); /// assert_eq!(b"".trim_ascii(), b""); /// ``` - #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] + #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn trim_ascii(&self) -> &[u8] { self.trim_ascii_start().trim_ascii_end() diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index b6f65907d3c30..669cdc92e3586 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2531,15 +2531,14 @@ impl str { /// # Examples /// /// ``` - /// #![feature(byte_slice_trim_ascii)] - /// /// assert_eq!(" \t \u{3000}hello world\n".trim_ascii_start(), "\u{3000}hello world\n"); /// assert_eq!(" ".trim_ascii_start(), ""); /// assert_eq!("".trim_ascii_start(), ""); /// ``` - #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] + #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn trim_ascii_start(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2557,15 +2556,14 @@ impl str { /// # Examples /// /// ``` - /// #![feature(byte_slice_trim_ascii)] - /// /// assert_eq!("\r hello world\u{3000}\n ".trim_ascii_end(), "\r hello world\u{3000}"); /// assert_eq!(" ".trim_ascii_end(), ""); /// assert_eq!("".trim_ascii_end(), ""); /// ``` - #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] + #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn trim_ascii_end(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate @@ -2584,15 +2582,14 @@ impl str { /// # Examples /// /// ``` - /// #![feature(byte_slice_trim_ascii)] - /// /// assert_eq!("\r hello world\n ".trim_ascii(), "hello world"); /// assert_eq!(" ".trim_ascii(), ""); /// assert_eq!("".trim_ascii(), ""); /// ``` - #[unstable(feature = "byte_slice_trim_ascii", issue = "94035")] #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] + #[stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "byte_slice_trim_ascii", since = "CURRENT_RUSTC_VERSION")] #[inline] pub const fn trim_ascii(&self) -> &str { // SAFETY: Removing ASCII characters from a `&str` does not invalidate From 4d63d0af4d312a81c50c6030335baed4e98bcd5c Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 11 May 2024 04:56:14 +0000 Subject: [PATCH 27/29] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 7a0ee91ba5523edde33e957f02595c468bb00eba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 11 May 2024 10:27:20 +0200 Subject: [PATCH 28/29] alloc: update comments around malloc() alignment --- src/tools/miri/src/shims/alloc.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 4eefb8b439b40..3cf443eea3ab5 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -22,20 +22,33 @@ 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 { 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 + // However, Windows `HeapAlloc` always aligns, even small allocations, so it gets different treatment here. + // Source: + if kind == MiriMemoryKind::WinHeap || 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; } From 01b151ebd4b74278342788c2530beb155c9a0ebc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 11 May 2024 10:48:36 +0200 Subject: [PATCH 29/29] separate windows heap functions from C heap shims --- src/tools/miri/src/lib.rs | 1 + src/tools/miri/src/shims/alloc.rs | 28 ++++------- src/tools/miri/src/shims/foreign_items.rs | 8 +-- .../miri/src/shims/unix/foreign_items.rs | 2 +- .../miri/src/shims/windows/foreign_items.rs | 50 +++++++++++++++---- 5 files changed, 56 insertions(+), 33 deletions(-) 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/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 3cf443eea3ab5..1deb9a5654edf 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -19,8 +19,8 @@ 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(); // 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 @@ -43,9 +43,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // - 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 - // However, Windows `HeapAlloc` always aligns, even small allocations, so it gets different treatment here. - // Source: - if kind == MiriMemoryKind::WinHeap || size >= max_fundamental_align { + 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. @@ -98,11 +96,10 @@ 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(); - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.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( @@ -114,14 +111,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { 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(()) } @@ -130,13 +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`. - self.malloc(new_size, /*zero_init*/ false, kind) + self.malloc(new_size, /*zero_init*/ false) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. @@ -148,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/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 1185d440ec681..d431c28d55a56 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -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)?; } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 595cf64a4e4b4..cec7f1bfd3873 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/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 44b0f25b55c0f..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::*; @@ -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)?; }