diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index d5ef9c7674147..925b85f58766c 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -228,7 +228,8 @@ and macOS targets are usually on par. Windows is supported less well. ### Running tests in parallel -Though it implements Rust threading, Miri itself is a single-threaded interpreter. +Though it implements Rust threading, Miri itself is a single-threaded interpreter +(it works like a multi-threaded OS on a single-core CPU). This means that when running `cargo miri test`, you will probably see a dramatic increase in the amount of time it takes to run your whole test suite due to the inherent interpreter slowdown and a loss of parallelism. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index c44422d758c57..a6ccd9bab3930 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f57b9e6f565a1847e83a63f3e90faa3870536c1f +b6fdaf2a15736cbccf248b532f48e33179614d40 diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 9816061a8ecae..a2795f233655b 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -710,7 +710,7 @@ fn main() { if !miri_config.native_lib.is_empty() && miri_config.provenance_mode == ProvenanceMode::Strict { fatal_error!("strict provenance is not compatible with calling native functions"); } - // Native calls and many-seeds are an "intersting" combination. + // Native calls and many-seeds are an "interesting" combination. if !miri_config.native_lib.is_empty() && many_seeds.is_some() { eprintln!( "warning: `-Zmiri-many-seeds` runs multiple instances of the program in the same address space, \ diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 571f5efc9739b..4c25caf56446a 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -2,7 +2,7 @@ use std::collections::hash_map::Entry; use std::io::Write; use std::path::Path; -use rustc_abi::{Align, CanonAbi, Size}; +use rustc_abi::{Align, CanonAbi, ExternAbi, Size}; use rustc_ast::expand::allocator::NO_ALLOC_SHIM_IS_UNSTABLE; use rustc_data_structures::either::Either; use rustc_hir::attrs::Linkage; @@ -435,6 +435,40 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Return value: 0 on success, otherwise the size it would have needed. this.write_int(if success { 0 } else { needed_size }, dest)?; } + "miri_thread_spawn" => { + // FIXME: `check_shim_sig` does not work with function pointers. + let [start_routine, func_arg] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; + let start_routine = this.read_pointer(start_routine)?; + let func_arg = this.read_immediate(func_arg)?; + + this.start_regular_thread( + Some(dest.clone()), + start_routine, + ExternAbi::Rust, + func_arg, + this.machine.layouts.unit, + )?; + } + "miri_thread_join" => { + let [thread_id] = this.check_shim_sig( + shim_sig!(extern "Rust" fn(usize) -> bool), + link_name, + abi, + args, + )?; + + let thread = this.read_target_usize(thread_id)?; + if let Ok(thread) = this.thread_id_try_from(thread) { + this.join_thread_exclusive( + thread, + /* success_retval */ Scalar::from_bool(true), + dest, + )?; + } else { + this.write_scalar(Scalar::from_bool(false), dest)?; + } + } // Hint that a loop is spinning indefinitely. "miri_spin_loop" => { let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs index bc5e7f584f500..4e4b1bc2b9fec 100644 --- a/src/tools/miri/src/shims/sig.rs +++ b/src/tools/miri/src/shims/sig.rs @@ -17,11 +17,18 @@ pub struct ShimSig<'tcx, const ARGS: usize> { /// Construct a `ShimSig` with convenient syntax: /// ```rust,ignore -/// shim_sig!(this, extern "C" fn (*const T, i32) -> usize) +/// shim_sig!(extern "C" fn (*const T, i32) -> usize) /// ``` +/// +/// The following types are supported: +/// - primitive integer types +/// - `()` +/// - (thin) raw pointers, written `*const _` and `*mut _` since the pointee type is irrelevant +/// - `$crate::$mod::...::$ty` for a type from the given crate (most commonly that is `libc`) +/// - `winapi::$ty` for a type from `std::sys::pal::windows::c` #[macro_export] macro_rules! shim_sig { - (extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => { + (extern $abi:literal fn($($arg:ty),* $(,)?) -> $ret:ty) => { |this| $crate::shims::sig::ShimSig { abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), args: [$(shim_sig_arg!(this, $arg)),*], @@ -50,9 +57,13 @@ macro_rules! shim_sig_arg { "u128" => $this.tcx.types.u128, "usize" => $this.tcx.types.usize, "()" => $this.tcx.types.unit, + "bool" => $this.tcx.types.bool, "*const _" => $this.machine.layouts.const_raw_ptr.ty, "*mut _" => $this.machine.layouts.mut_raw_ptr.ty, - ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty, + ty if let Some(win_ty) = ty.strip_prefix("winapi::") => + $this.windows_ty_layout(win_ty).ty, + ty if ty.contains("::") => + helpers::path_ty_layout($this, &ty.split("::").collect::>()).ty, ty => panic!("unsupported signature type {ty:?}"), } }}; diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 2b290b68c78c6..f00bfb0a20787 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -28,21 +28,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // File related shims "stat" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; } "readdir" => { + // FIXME: This does not have a direct test (#3179). let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; } "pread64" => { + // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), link_name, @@ -56,6 +60,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, Some(offset), dest)?; } "pwrite64" => { + // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), link_name, @@ -70,6 +75,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, Some(offset), dest)?; } "lseek64" => { + // FIXME: This does not have a direct test (#3179). let [fd, offset, whence] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t), link_name, diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 8eacdc3583d44..04ec260eccd00 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -143,6 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "getcwd" => { + // FIXME: This does not have a direct test (#3179). let [buf, size] = this.check_shim_sig( shim_sig!(extern "C" fn(*mut _, usize) -> *mut _), link_name, @@ -153,6 +154,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(result, dest)?; } "chdir" => { + // FIXME: This does not have a direct test (#3179). let [path] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _) -> i32), link_name, @@ -209,6 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, None, dest)?; } "pread" => { + // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize), link_name, @@ -222,6 +225,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, Some(offset), dest)?; } "pwrite" => { + // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize), link_name, @@ -299,6 +303,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "unlink" => { + // FIXME: This does not have a direct test (#3179). let [path] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _) -> i32), link_name, @@ -309,6 +314,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "symlink" => { + // FIXME: This does not have a direct test (#3179). let [target, linkpath] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _, *const _) -> i32), link_name, @@ -324,6 +330,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "rename" => { + // FIXME: This does not have a direct test (#3179). let [oldpath, newpath] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _, *const _) -> i32), link_name, @@ -334,6 +341,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "mkdir" => { + // FIXME: This does not have a direct test (#3179). let [path, mode] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _, libc::mode_t) -> i32), link_name, @@ -344,6 +352,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "rmdir" => { + // FIXME: This does not have a direct test (#3179). let [path] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _) -> i32), link_name, @@ -354,6 +363,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "opendir" => { + // FIXME: This does not have a direct test (#3179). let [name] = this.check_shim_sig( shim_sig!(extern "C" fn(*const _) -> *mut _), link_name, @@ -364,6 +374,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "closedir" => { + // FIXME: This does not have a direct test (#3179). let [dirp] = this.check_shim_sig( shim_sig!(extern "C" fn(*mut _) -> i32), link_name, @@ -374,6 +385,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "lseek" => { + // FIXME: This does not have a direct test (#3179). let [fd, offset, whence] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t), link_name, @@ -398,6 +410,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "fsync" => { + // FIXME: This does not have a direct test (#3179). let [fd] = this.check_shim_sig( shim_sig!(extern "C" fn(i32) -> i32), link_name, @@ -408,6 +421,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "fdatasync" => { + // FIXME: This does not have a direct test (#3179). let [fd] = this.check_shim_sig( shim_sig!(extern "C" fn(i32) -> i32), link_name, @@ -659,6 +673,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "pthread_key_delete" => { + // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; this.machine.tls.delete_tls_key(key)?; @@ -666,6 +681,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "pthread_getspecific" => { + // FIXME: This does not have a direct test (#3179). let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); @@ -673,6 +689,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { + // FIXME: This does not have a direct test (#3179). let [key, new_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; @@ -833,6 +850,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "sched_yield" => { + // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.sched_yield()?; this.write_null(dest)?; @@ -941,6 +959,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "pthread_atfork" => { + // FIXME: This does not have a direct test (#3179). let [prepare, parent, child] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_pointer(prepare)?; diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index fb2d3f7584209..b94ee27c46a00 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -139,11 +139,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For those, we both intercept `func` and `call@FBSD_1.0` symbols cases // since freebsd 12 the former form can be expected. "stat" | "stat@FBSD_1.0" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat@FBSD_1.0" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; @@ -154,6 +156,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "readdir" | "readdir@FBSD_1.0" => { + // FIXME: This does not have a direct test (#3179). let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; 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 a7cb2ed11b2b3..426bc28ce8878 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -45,6 +45,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "pread64" => { + // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), link_name, @@ -58,6 +59,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, Some(offset), dest)?; } "pwrite64" => { + // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), link_name, @@ -72,6 +74,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, Some(offset), dest)?; } "lseek64" => { + // FIXME: This does not have a direct test (#3179). let [fd, offset, whence] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t), link_name, @@ -111,6 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "readdir64" => { + // FIXME: This does not have a direct test (#3179). let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.readdir64("dirent64", dirp)?; this.write_scalar(result, dest)?; @@ -122,6 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "statx" => { + // FIXME: This does not have a direct test (#3179). let [dirfd, pathname, flags, mask, statxbuf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index f798f64441b1b..204934f4c41dc 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -47,11 +47,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "stat" | "stat$INODE64" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat$INODE64" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; @@ -62,11 +64,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "opendir$INODE64" => { + // FIXME: This does not have a direct test (#3179). let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r$INODE64" => { + // FIXME: This does not have a direct test (#3179). let [dirp, entry, result] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_readdir_r(dirp, entry, result)?; @@ -87,6 +91,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Environment related shims "_NSGetEnviron" => { + // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let environ = this.machine.env_vars.unix().environ(); this.write_pointer(environ, dest)?; @@ -111,6 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "mach_timebase_info" => { + // FIXME: This does not have a direct test (#3179). let [info] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.mach_timebase_info(info)?; this.write_scalar(result, dest)?; @@ -118,14 +124,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Access to command-line arguments "_NSGetArgc" => { + // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?; } "_NSGetArgv" => { + // FIXME: This does not have a direct test (#3179). let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?; } "_NSGetExecutablePath" => { + // FIXME: This does not have a direct test (#3179). let [buf, bufsize] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`_NSGetExecutablePath`")?; @@ -168,12 +177,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "pthread_get_stackaddr_np" => { + // FIXME: This does not have a direct test (#3179). let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { + // FIXME: This does not have a direct test (#3179). let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size()); 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 fa8c86b025a7a..f3918fdccf128 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -91,16 +91,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "stat" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" => { + // FIXME: This does not have a direct test (#3179). let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.lstat(path, buf)?; this.write_scalar(result, dest)?; } "readdir" => { + // FIXME: This does not have a direct test (#3179). let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; @@ -122,6 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "stack_getbounds" => { + // FIXME: This does not have a direct test (#3179). let [stack] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?; @@ -140,6 +144,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pset_info" => { + // FIXME: This does not have a direct test (#3179). let [pset, tpe, cpus, list] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // We do not need to handle the current process cpu mask, available_parallelism diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 3bc52dddfe8b8..0bdf6bb785056 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -2,11 +2,11 @@ use std::ffi::OsStr; use std::path::{self, Path, PathBuf}; use std::{io, iter, str}; -use rustc_abi::{Align, CanonAbi, Size, X86Call}; +use rustc_abi::{Align, Size}; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use rustc_target::spec::{Arch, Env}; +use rustc_target::spec::Env; use self::shims::windows::handle::{Handle, PseudoHandle}; use crate::shims::os_str::bytes_to_os_str; @@ -137,65 +137,101 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); - // According to - // https://github.com/rust-lang/rust/blob/fb00adbdb69266f10df95a4527b767b0ad35ea48/compiler/rustc_target/src/spec/mod.rs#L2766-L2768, - // x86-32 Windows uses a different calling convention than other Windows targets - // for the "system" ABI. - let sys_conv = if this.tcx.sess.target.arch == Arch::X86 { - CanonAbi::X86(X86Call::Stdcall) - } else { - CanonAbi::C - }; - // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. // Windows API stubs. - // HANDLE = isize - // NTSTATUS = LONH = i32 + // HANDLE = *mut c_void (formerly: isize) + // NTSTATUS = LONG = i32 // DWORD = ULONG = u32 // BOOL = i32 // BOOLEAN = u8 match link_name.as_str() { // Environment related shims "GetEnvironmentVariableW" => { - let [name, buf, size] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [name, buf, size] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _, *mut _, u32) -> u32), + link_name, + abi, + args, + )?; let result = this.GetEnvironmentVariableW(name, buf, size)?; this.write_scalar(result, dest)?; } "SetEnvironmentVariableW" => { - let [name, value] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [name, value] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _, *const _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let result = this.SetEnvironmentVariableW(name, value)?; this.write_scalar(result, dest)?; } "GetEnvironmentStringsW" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> *mut _), + link_name, + abi, + args, + )?; let result = this.GetEnvironmentStringsW()?; this.write_pointer(result, dest)?; } "FreeEnvironmentStringsW" => { - let [env_block] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [env_block] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let result = this.FreeEnvironmentStringsW(env_block)?; this.write_scalar(result, dest)?; } "GetCurrentDirectoryW" => { - let [size, buf] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [size, buf] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32, *mut _) -> u32), + link_name, + abi, + args, + )?; let result = this.GetCurrentDirectoryW(size, buf)?; this.write_scalar(result, dest)?; } "SetCurrentDirectoryW" => { - let [path] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [path] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let result = this.SetCurrentDirectoryW(path)?; this.write_scalar(result, dest)?; } "GetUserProfileDirectoryW" => { - let [token, buf, size] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [token, buf, size] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *mut _, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let result = this.GetUserProfileDirectoryW(token, buf, size)?; this.write_scalar(result, dest)?; } "GetCurrentProcessId" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> u32), + link_name, + abi, + args, + )?; let result = this.GetCurrentProcessId()?; this.write_scalar(result, dest)?; } @@ -212,7 +248,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { n, byte_offset, key, - ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig( + shim_sig!( + extern "system" fn( + winapi::HANDLE, + winapi::HANDLE, + *mut _, + *mut _, + *mut _, + *mut _, + u32, + *mut _, + *mut _, + ) -> i32 + ), + link_name, + abi, + args, + )?; this.NtWriteFile( handle, event, @@ -237,7 +290,24 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { n, byte_offset, key, - ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig( + shim_sig!( + extern "system" fn( + winapi::HANDLE, + winapi::HANDLE, + *mut _, + *mut _, + *mut _, + *mut _, + u32, + *mut _, + *mut _, + ) -> i32 + ), + link_name, + abi, + args, + )?; this.NtReadFile( handle, event, @@ -252,8 +322,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "GetFullPathNameW" => { - let [filename, size, buffer, filepart] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [filename, size, buffer, filepart] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _, u32, *mut _, *mut _) -> u32), + link_name, + abi, + args, + )?; this.check_no_isolation("`GetFullPathNameW`")?; let filename = this.read_pointer(filename)?; @@ -290,7 +365,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { creation_disposition, flags_and_attributes, template_file, - ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig( + shim_sig!( + extern "system" fn( + *const _, + u32, + u32, + *mut _, + u32, + u32, + winapi::HANDLE, + ) -> winapi::HANDLE + ), + link_name, + abi, + args, + )?; let handle = this.CreateFileW( file_name, desired_access, @@ -303,29 +393,60 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(handle.to_scalar(this), dest)?; } "GetFileInformationByHandle" => { - let [handle, info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle, info] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let res = this.GetFileInformationByHandle(handle, info)?; this.write_scalar(res, dest)?; } "SetFileInformationByHandle" => { - let [handle, class, info, size] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle, class, info, size] = this.check_shim_sig( + shim_sig!( + extern "system" fn( + winapi::HANDLE, + winapi::FILE_INFO_BY_HANDLE_CLASS, + *mut _, + u32, + ) -> winapi::BOOL + ), + link_name, + abi, + args, + )?; let res = this.SetFileInformationByHandle(handle, class, info, size)?; this.write_scalar(res, dest)?; } "FlushFileBuffers" => { - let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE) -> winapi::BOOL), + link_name, + abi, + args, + )?; let res = this.FlushFileBuffers(handle)?; this.write_scalar(res, dest)?; } "DeleteFileW" => { - let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [file_name] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let res = this.DeleteFileW(file_name)?; this.write_scalar(res, dest)?; } "SetFilePointerEx" => { - let [file, distance_to_move, new_file_pointer, move_method] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [file, distance_to_move, new_file_pointer, move_method] = this.check_shim_sig( + // i64 is actually a LARGE_INTEGER union of {u32, i32} and {i64} + shim_sig!(extern "system" fn(winapi::HANDLE, i64, *mut _, u32) -> winapi::BOOL), + link_name, + abi, + args, + )?; let res = this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?; this.write_scalar(res, dest)?; @@ -333,8 +454,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "HeapAlloc" => { - let [handle, flags, size] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [handle, flags, size] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, u32, usize) -> *mut _), + link_name, + abi, + args, + )?; this.read_target_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; let size = this.read_target_usize(size)?; @@ -356,8 +482,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr, dest)?; } "HeapFree" => { - let [handle, flags, ptr] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [handle, flags, ptr] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, u32, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; @@ -369,8 +500,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(1), dest)?; } "HeapReAlloc" => { - let [handle, flags, old_ptr, size] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [handle, flags, old_ptr, size] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, u32, *mut _, usize) -> *mut _), + link_name, + abi, + args, + )?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let old_ptr = this.read_pointer(old_ptr)?; @@ -390,7 +526,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(new_ptr, dest)?; } "LocalFree" => { - let [ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [ptr] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HLOCAL) -> winapi::HLOCAL), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL." // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree) @@ -402,17 +544,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // errno "SetLastError" => { - let [error] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [error] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32) -> ()), + link_name, + abi, + args, + )?; let error = this.read_scalar(error)?; this.set_last_error(error)?; } "GetLastError" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> u32), + link_name, + abi, + args, + )?; let last_error = this.get_last_error()?; this.write_scalar(last_error, dest)?; } "RtlNtStatusToDosError" => { - let [status] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [status] = this.check_shim_sig( + shim_sig!(extern "system" fn(i32) -> u32), + link_name, + abi, + args, + )?; let status = this.read_scalar(status)?.to_u32()?; let err = match status { // STATUS_MEDIA_WRITE_PROTECTED => ERROR_WRITE_PROTECT @@ -433,8 +590,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "GetSystemInfo" => { + // FIXME: This does not have a direct test (#3179). // Also called from `page_size` crate. - let [system_info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [system_info] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> ()), + link_name, + abi, + args, + )?; let system_info = this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?; // Initialize with `0`. @@ -457,19 +620,34 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This just creates a key; Windows does not natively support TLS destructors. // Create key and return it. - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> u32), + link_name, + abi, + args, + )?; let key = this.machine.tls.create_tls_key(None, dest.layout.size)?; this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { - let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [key] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32) -> *mut _), + link_name, + abi, + args, + )?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "TlsSetValue" => { - let [key, new_ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [key, new_ptr] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -479,7 +657,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } "TlsFree" => { - let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [key] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32) -> winapi::BOOL), + link_name, + abi, + args, + )?; let key = u128::from(this.read_scalar(key)?.to_u32()?); this.machine.tls.delete_tls_key(key)?; @@ -489,7 +672,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Access to command-line arguments "GetCommandLineW" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> *mut _), + link_name, + abi, + args, + )?; this.write_pointer( this.machine.cmd_line.expect("machine must be initialized"), dest, @@ -498,31 +687,56 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time related shims "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => { - #[allow(non_snake_case)] - let [LPFILETIME] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; - this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?; + // FIXME: This does not have a direct test (#3179). + let [filetime] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> ()), + link_name, + abi, + args, + )?; + this.GetSystemTimeAsFileTime(link_name.as_str(), filetime)?; } "QueryPerformanceCounter" => { - #[allow(non_snake_case)] - let [lpPerformanceCount] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; - let result = this.QueryPerformanceCounter(lpPerformanceCount)?; + // FIXME: This does not have a direct test (#3179). + let [performance_count] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; + let result = this.QueryPerformanceCounter(performance_count)?; this.write_scalar(result, dest)?; } "QueryPerformanceFrequency" => { - #[allow(non_snake_case)] - let [lpFrequency] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; - let result = this.QueryPerformanceFrequency(lpFrequency)?; + // FIXME: This does not have a direct test (#3179). + let [frequency] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; + let result = this.QueryPerformanceFrequency(frequency)?; this.write_scalar(result, dest)?; } "Sleep" => { - let [timeout] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [timeout] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32) -> ()), + link_name, + abi, + args, + )?; this.Sleep(timeout)?; } "CreateWaitableTimerExW" => { - let [attributes, name, flags, access] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [attributes, name, flags, access] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _, *const _, u32, u32) -> winapi::HANDLE), + link_name, + abi, + args, + )?; this.read_pointer(attributes)?; this.read_pointer(name)?; this.read_scalar(flags)?.to_u32()?; @@ -535,40 +749,70 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "InitOnceBeginInitialize" => { - let [ptr, flags, pending, context] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [ptr, flags, pending, context] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _, u32, *mut _, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?; } "InitOnceComplete" => { - let [ptr, flags, context] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [ptr, flags, context] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _, u32, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; let result = this.InitOnceComplete(ptr, flags, context)?; this.write_scalar(result, dest)?; } "WaitOnAddress" => { - let [ptr_op, compare_op, size_op, timeout_op] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [ptr_op, compare_op, size_op, timeout_op] = this.check_shim_sig( + // First pointer is volatile + shim_sig!(extern "system" fn(*mut _, *mut _, usize, u32) -> winapi::BOOL), + link_name, + abi, + args, + )?; this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?; } "WakeByAddressSingle" => { - let [ptr_op] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [ptr_op] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> ()), + link_name, + abi, + args, + )?; this.WakeByAddressSingle(ptr_op)?; } "WakeByAddressAll" => { - let [ptr_op] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [ptr_op] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> ()), + link_name, + abi, + args, + )?; this.WakeByAddressAll(ptr_op)?; } // Dynamic symbol loading "GetProcAddress" => { - #[allow(non_snake_case)] - let [hModule, lpProcName] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; - this.read_target_isize(hModule)?; - let name = this.read_c_str(this.read_pointer(lpProcName)?)?; + // FIXME: This does not have a direct test (#3179). + let [module, proc_name] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HMODULE, *const _) -> winapi::FARPROC), + link_name, + abi, + args, + )?; + this.read_target_isize(module)?; + let name = this.read_c_str(this.read_pointer(proc_name)?)?; if let Ok(name) = str::from_utf8(name) && is_dyn_sym(name) { @@ -581,8 +825,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "CreateThread" => { - let [security, stacksize, start, arg, flags, thread] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [security, stacksize, start, arg, flags, thread] = this.check_shim_sig( + shim_sig!( + extern "system" fn( + *mut _, + usize, + *mut _, + *mut _, + u32, + *mut _, + ) -> winapi::HANDLE + ), + link_name, + abi, + args, + )?; let thread_id = this.CreateThread(security, stacksize, start, arg, flags, thread)?; @@ -590,13 +847,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?; } "WaitForSingleObject" => { - let [handle, timeout] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle, timeout] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, u32) -> u32), + link_name, + abi, + args, + )?; this.WaitForSingleObject(handle, timeout, dest)?; } "GetCurrentProcess" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> winapi::HANDLE), + link_name, + abi, + args, + )?; this.write_scalar( Handle::Pseudo(PseudoHandle::CurrentProcess).to_scalar(this), @@ -604,7 +870,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "GetCurrentThread" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> winapi::HANDLE), + link_name, + abi, + args, + )?; this.write_scalar( Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this), @@ -612,7 +883,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "SetThreadDescription" => { - let [handle, name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle, name] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *const _) -> i32), + link_name, + abi, + args, + )?; let handle = this.read_handle(handle, "SetThreadDescription")?; let name = this.read_wide_str(this.read_pointer(name)?)?; @@ -627,8 +903,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u32(0), dest)?; } "GetThreadDescription" => { - let [handle, name_ptr] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle, name_ptr] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> i32), + link_name, + abi, + args, + )?; let handle = this.read_handle(handle, "GetThreadDescription")?; let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name @@ -651,7 +931,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "GetThreadId" => { - let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE) -> u32), + link_name, + abi, + args, + )?; let handle = this.read_handle(handle, "GetThreadId")?; let thread = match handle { Handle::Thread(thread) => thread, @@ -662,7 +947,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u32(tid), dest)?; } "GetCurrentThreadId" => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> u32), + link_name, + abi, + args, + )?; let thread = this.active_thread(); let tid = this.get_tid(thread); this.write_scalar(Scalar::from_u32(tid), dest)?; @@ -670,7 +960,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "ExitProcess" => { - let [code] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [code] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32) -> ()), + link_name, + abi, + args, + )?; // Windows technically uses u32, but we unify everything to a Unix-style i32. let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); @@ -678,15 +974,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SystemFunction036" => { // used by getrandom 0.1 // This is really 'RtlGenRandom'. - let [ptr, len] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [ptr, len] = this.check_shim_sig( + // Returns winapi::BOOLEAN, which is a byte + shim_sig!(extern "system" fn(*mut _, u32) -> u8), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_u32()?; this.gen_random(ptr, len.into())?; this.write_scalar(Scalar::from_bool(true), dest)?; } "ProcessPrng" => { + // FIXME: This does not have a direct test (#3179). // used by `std` - let [ptr, len] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [ptr, len] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _, usize) -> winapi::BOOL), + link_name, + abi, + args, + )?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -694,8 +1002,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "BCryptGenRandom" => { // used by getrandom 0.2 - let [algorithm, ptr, len, flags] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [algorithm, ptr, len, flags] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _, *mut _, u32, u32) -> i32), + link_name, + abi, + args, + )?; let algorithm = this.read_scalar(algorithm)?; let algorithm = algorithm.to_target_usize(this)?; let ptr = this.read_pointer(ptr)?; @@ -728,9 +1040,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; // STATUS_SUCCESS } "GetConsoleScreenBufferInfo" => { + // FIXME: This does not have a direct test (#3179). // `term` needs this, so we fake it. - let [console, buffer_info] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [console, buffer_info] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; this.read_target_isize(console)?; // FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std this.deref_pointer(buffer_info)?; @@ -739,13 +1056,34 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "GetStdHandle" => { - let [which] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [which] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32) -> winapi::HANDLE), + link_name, + abi, + args, + )?; let res = this.GetStdHandle(which)?; this.write_scalar(res, dest)?; } "DuplicateHandle" => { let [src_proc, src_handle, target_proc, target_handle, access, inherit, options] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + this.check_shim_sig( + shim_sig!( + extern "system" fn( + winapi::HANDLE, + winapi::HANDLE, + winapi::HANDLE, + *mut _, + u32, + winapi::BOOL, + u32, + ) -> winapi::BOOL + ), + link_name, + abi, + args, + )?; let res = this.DuplicateHandle( src_proc, src_handle, @@ -758,15 +1096,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "CloseHandle" => { - let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE) -> winapi::BOOL), + link_name, + abi, + args, + )?; let ret = this.CloseHandle(handle)?; this.write_scalar(ret, dest)?; } "GetModuleFileNameW" => { - let [handle, filename, size] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [handle, filename, size] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HMODULE, *mut _, u32) -> u32), + link_name, + abi, + args, + )?; this.check_no_isolation("`GetModuleFileNameW`")?; let handle = this.read_handle(handle, "GetModuleFileNameW")?; @@ -799,8 +1147,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "FormatMessageW" => { - let [flags, module, message_id, language_id, buffer, size, arguments] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + // FIXME: This does not have a direct test (#3179). + let [flags, module, message_id, language_id, buffer, size, arguments] = this + .check_shim_sig( + shim_sig!( + extern "system" fn(u32, *const _, u32, u32, *mut _, u32, *mut _) -> u32 + ), + link_name, + abi, + args, + )?; let flags = this.read_scalar(flags)?.to_u32()?; let _module = this.read_pointer(module)?; // seems to contain a module name @@ -833,6 +1189,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "_Unwind_RaiseException" => { + // FIXME: This does not have a direct test (#3179). // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. // It was originally specified as part of the Itanium C++ ABI: // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. @@ -843,7 +1200,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let [payload] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> unwind::libunwind::_Unwind_Reason_Code), + link_name, + abi, + args, + )?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } @@ -851,56 +1213,86 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame_in_std() => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> winapi::HANDLE), + link_name, + abi, + args, + )?; // Just fake a HANDLE // It's fine to not use the Handle type here because its a stub this.write_int(1, dest)?; } "GetModuleHandleA" if this.frame_in_std() => { - #[allow(non_snake_case)] - let [_lpModuleName] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [_module_name] = this.check_shim_sig( + shim_sig!(extern "system" fn(*const _) -> winapi::HMODULE), + link_name, + abi, + args, + )?; // We need to return something non-null here to make `compat_fn!` work. this.write_int(1, dest)?; } "SetConsoleTextAttribute" if this.frame_in_std() => { - #[allow(non_snake_case)] - let [_hConsoleOutput, _wAttribute] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [_console_output, _attribute] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, u16) -> winapi::BOOL), + link_name, + abi, + args, + )?; // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } "GetConsoleMode" if this.frame_in_std() => { - let [console, mode] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [console, mode] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; this.read_target_isize(console)?; this.deref_pointer_as(mode, this.machine.layouts.u32)?; // Indicate an error. this.write_null(dest)?; } "GetFileType" if this.frame_in_std() => { - #[allow(non_snake_case)] - let [_hFile] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [_file] = this.check_shim_sig( + shim_sig!(extern "system" fn(winapi::HANDLE) -> u32), + link_name, + abi, + args, + )?; // Return unknown file type. this.write_null(dest)?; } "AddVectoredExceptionHandler" if this.frame_in_std() => { - #[allow(non_snake_case)] - let [_First, _Handler] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [_first, _handler] = this.check_shim_sig( + shim_sig!(extern "system" fn(u32, *mut _) -> *mut _), + link_name, + abi, + args, + )?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_int(1, dest)?; } "SetThreadStackGuarantee" if this.frame_in_std() => { - #[allow(non_snake_case)] - let [_StackSizeInBytes] = - this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [_stack_size_in_bytes] = this.check_shim_sig( + shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL), + link_name, + abi, + args, + )?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_int(1, dest)?; } // this is only callable from std because we know that std ignores the return value "SwitchToThread" if this.frame_in_std() => { - let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig( + shim_sig!(extern "system" fn() -> winapi::BOOL), + link_name, + abi, + args, + )?; this.yield_active_thread(); diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 97b9f649c1580..7d8e52db73d64 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi; use super::{ ShiftOp, horizontal_bin_op, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, - pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar, + pmaddwd, pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar, }; use crate::*; @@ -232,33 +232,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(left_len, right_len); - assert_eq!(dest_len.strict_mul(2), left_len); - - for i in 0..dest_len { - let j1 = i.strict_mul(2); - let left1 = this.read_scalar(&this.project_index(&left, j1)?)?.to_i16()?; - let right1 = this.read_scalar(&this.project_index(&right, j1)?)?.to_i16()?; - - let j2 = j1.strict_add(1); - let left2 = this.read_scalar(&this.project_index(&left, j2)?)?.to_i16()?; - let right2 = this.read_scalar(&this.project_index(&right, j2)?)?.to_i16()?; - - let dest = this.project_index(&dest, i)?; - - // Multiplications are i16*i16->i32, which will not overflow. - let mul1 = i32::from(left1).strict_mul(right1.into()); - let mul2 = i32::from(left2).strict_mul(right2.into()); - // However, this addition can overflow in the most extreme case - // (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000 - let res = mul1.wrapping_add(mul2); - - this.write_scalar(Scalar::from_i32(res), &dest)?; - } + pmaddwd(this, left, right, dest)?; } _ => return interp_ok(EmulateItemResult::NotSupported), } diff --git a/src/tools/miri/src/shims/x86/avx512.rs b/src/tools/miri/src/shims/x86/avx512.rs index 0466ba1bd6c01..b057a78b6c8ee 100644 --- a/src/tools/miri/src/shims/x86/avx512.rs +++ b/src/tools/miri/src/shims/x86/avx512.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use super::{permute, pmaddbw, psadbw, pshufb}; +use super::{packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, pmaddwd, psadbw, pshufb}; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -88,6 +88,15 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { psadbw(this, left, right, dest)? } + // Used to implement the _mm512_madd_epi16 function. + "pmaddw.d.512" => { + this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?; + + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + pmaddwd(this, left, right, dest)?; + } // Used to implement the _mm512_maddubs_epi16 function. "pmaddubs.w.512" => { let [left, right] = @@ -121,6 +130,38 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { vpdpbusd(this, src, a, b, dest)?; } + // Used to implement the _mm512_packs_epi16 function + "packsswb.512" => { + this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?; + + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + packsswb(this, a, b, dest)?; + } + // Used to implement the _mm512_packus_epi16 function + "packuswb.512" => { + this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?; + + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + packuswb(this, a, b, dest)?; + } + // Used to implement the _mm512_packs_epi32 function + "packssdw.512" => { + this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?; + + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + packssdw(this, a, b, dest)?; + } + // Used to implement the _mm512_packus_epi32 function + "packusdw.512" => { + this.expect_target_feature_for_intrinsic(link_name, "avx512bw")?; + + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + packusdw(this, a, b, dest)?; + } _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index a5164cc87ab4d..dc0d8d48ac9b1 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -964,6 +964,52 @@ fn psadbw<'tcx>( interp_ok(()) } +/// Multiply packed signed 16-bit integers in `left` and `right`, producing intermediate signed 32-bit integers. +/// Horizontally add adjacent pairs of intermediate 32-bit integers, and pack the results in `dest`. +/// +/// +/// +/// +fn pmaddwd<'tcx>( + ecx: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = ecx.project_to_simd(left)?; + let (right, right_len) = ecx.project_to_simd(right)?; + let (dest, dest_len) = ecx.project_to_simd(dest)?; + + // fn pmaddwd(a: i16x8, b: i16x8) -> i32x4; + // fn pmaddwd(a: i16x16, b: i16x16) -> i32x8; + // fn vpmaddwd(a: i16x32, b: i16x32) -> i32x16; + assert_eq!(left_len, right_len); + assert_eq!(dest_len.strict_mul(2), left_len); + + for i in 0..dest_len { + let j1 = i.strict_mul(2); + let left1 = ecx.read_scalar(&ecx.project_index(&left, j1)?)?.to_i16()?; + let right1 = ecx.read_scalar(&ecx.project_index(&right, j1)?)?.to_i16()?; + + let j2 = j1.strict_add(1); + let left2 = ecx.read_scalar(&ecx.project_index(&left, j2)?)?.to_i16()?; + let right2 = ecx.read_scalar(&ecx.project_index(&right, j2)?)?.to_i16()?; + + let dest = ecx.project_index(&dest, i)?; + + // Multiplications are i16*i16->i32, which will not overflow. + let mul1 = i32::from(left1).strict_mul(right1.into()); + let mul2 = i32::from(left2).strict_mul(right2.into()); + // However, this addition can overflow in the most extreme case + // (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000 + let res = mul1.wrapping_add(mul2); + + ecx.write_scalar(Scalar::from_i32(res), &dest)?; + } + + interp_ok(()) +} + /// Multiplies packed 8-bit unsigned integers from `left` and packed /// signed 8-bit integers from `right` into 16-bit signed integers. Then, /// the saturating sum of the products with indices `2*i` and `2*i+1` diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 3fbab9ba789ee..f712814a5eda8 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi; use super::{ FloatBinOp, ShiftOp, bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, - packssdw, packsswb, packuswb, psadbw, shift_simd_by_scalar, + packssdw, packsswb, packuswb, pmaddwd, psadbw, shift_simd_by_scalar, }; use crate::*; @@ -286,33 +286,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(left_len, right_len); - assert_eq!(dest_len.strict_mul(2), left_len); - - for i in 0..dest_len { - let j1 = i.strict_mul(2); - let left1 = this.read_scalar(&this.project_index(&left, j1)?)?.to_i16()?; - let right1 = this.read_scalar(&this.project_index(&right, j1)?)?.to_i16()?; - - let j2 = j1.strict_add(1); - let left2 = this.read_scalar(&this.project_index(&left, j2)?)?.to_i16()?; - let right2 = this.read_scalar(&this.project_index(&right, j2)?)?.to_i16()?; - - let dest = this.project_index(&dest, i)?; - - // Multiplications are i16*i16->i32, which will not overflow. - let mul1 = i32::from(left1).strict_mul(right1.into()); - let mul2 = i32::from(left2).strict_mul(right2.into()); - // However, this addition can overflow in the most extreme case - // (-0x8000)*(-0x8000)+(-0x8000)*(-0x8000) = 0x80000000 - let res = mul1.wrapping_add(mul2); - - this.write_scalar(Scalar::from_i32(res), &dest)?; - } + pmaddwd(this, left, right, dest)?; } _ => return interp_ok(EmulateItemResult::NotSupported), } diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs index da549a8d117d0..a71778b1d0d49 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs @@ -12,7 +12,7 @@ use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject}; // XXX HACK: This is how miri represents the handle for thread 0. // This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle` -// but miri does not implement `DuplicateHandle` yet. +// but miri does not implement `DuplicateHandle` yet. (FIXME: it does now.) const MAIN_THREAD: HANDLE = (2i32 << 29) as HANDLE; fn main() { diff --git a/src/tools/miri/tests/pass-dep/getrandom.rs b/src/tools/miri/tests/pass-dep/getrandom.rs index d359730e7f971..9adb48fb1c3c3 100644 --- a/src/tools/miri/tests/pass-dep/getrandom.rs +++ b/src/tools/miri/tests/pass-dep/getrandom.rs @@ -12,6 +12,8 @@ fn main() { #[cfg(not(target_os = "solaris"))] getrandom_01::getrandom(&mut data).unwrap(); + // On Windows, getrandom 0.2 uses the wrong return type for BCryptGenRandom + #[cfg(not(target_os = "windows"))] getrandom_02::getrandom(&mut data).unwrap(); getrandom_03::fill(&mut data).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/close-std-streams.rs b/src/tools/miri/tests/pass-dep/libc/close-std-streams.rs new file mode 100644 index 0000000000000..cc30d9557fc6b --- /dev/null +++ b/src/tools/miri/tests/pass-dep/libc/close-std-streams.rs @@ -0,0 +1,13 @@ +//@ignore-target: windows # no libc +//@ revisions: default null +//@[null] compile-flags: -Zmiri-mute-stdout-stderr + +fn main() { + // This is std library UB, but that's not relevant since we're + // only interacting with libc here. + unsafe { + libc::close(0); + libc::close(1); + libc::close(2); + } +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs index 400e3ca3d7db3..87ef0510af4f6 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs @@ -7,6 +7,10 @@ use std::mem::{size_of, size_of_val}; use libc::{cpu_set_t, sched_getaffinity, sched_setaffinity}; +#[path = "../../utils/libc.rs"] +mod libc_utils; +use libc_utils::errno_check; + // If pid is zero, then the calling thread is used. const PID: i32 = 0; @@ -41,8 +45,7 @@ fn configure_unavailable_cpu() { // Safety: valid value for this type let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }); // by default, only available CPUs are configured for i in 0..cpu_count { @@ -53,11 +56,9 @@ fn configure_unavailable_cpu() { // configure CPU that we don't have unsafe { libc::CPU_SET(cpu_count, &mut cpuset) }; - let err = unsafe { sched_setaffinity(PID, size_of::(), &cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_setaffinity(PID, size_of::(), &cpuset) }); - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }); // the CPU is not set because it is not available assert!(!unsafe { libc::CPU_ISSET(cpu_count, &cpuset) }); @@ -70,11 +71,11 @@ fn large_set() { // i.e. this has 2048 bits, twice the standard number let mut cpuset = [u64::MAX; 32]; - let err = unsafe { sched_setaffinity(PID, size_of_val(&cpuset), cpuset.as_ptr().cast()) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_setaffinity(PID, size_of_val(&cpuset), cpuset.as_ptr().cast()) }); - let err = unsafe { sched_getaffinity(PID, size_of_val(&cpuset), cpuset.as_mut_ptr().cast()) }; - assert_eq!(err, 0); + errno_check(unsafe { + sched_getaffinity(PID, size_of_val(&cpuset), cpuset.as_mut_ptr().cast()) + }); } fn get_small_cpu_mask() { @@ -91,8 +92,7 @@ fn get_small_cpu_mask() { assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput); } else { // other whole multiples of the size of c_ulong works - let err = unsafe { sched_getaffinity(PID, i, &mut cpuset) }; - assert_eq!(err, 0, "fail for {i}"); + errno_check(unsafe { sched_getaffinity(PID, i, &mut cpuset) }); } // anything else returns an error @@ -107,8 +107,7 @@ fn get_small_cpu_mask() { fn set_small_cpu_mask() { let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }); // setting a mask of size 0 is invalid let err = unsafe { sched_setaffinity(PID, 0, &cpuset) }; @@ -122,8 +121,7 @@ fn set_small_cpu_mask() { if cfg!(target_endian = "little") { 1 } else { core::mem::size_of::() }; for i in cpu_zero_included_length..24 { - let err = unsafe { sched_setaffinity(PID, i, &cpuset) }; - assert_eq!(err, 0, "fail for {i}"); + errno_check(unsafe { sched_setaffinity(PID, i, &cpuset) }); } } @@ -135,8 +133,7 @@ fn set_custom_cpu_mask() { let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; // at the start, thread 1 should be set - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }); assert!(unsafe { libc::CPU_ISSET(1, &cpuset) }); // make a valid mask @@ -144,12 +141,10 @@ fn set_custom_cpu_mask() { unsafe { libc::CPU_SET(0, &mut cpuset) }; // giving a smaller mask is fine - let err = unsafe { sched_setaffinity(PID, 8, &cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_setaffinity(PID, 8, &cpuset) }); // and actually disables other threads - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }); assert!(unsafe { !libc::CPU_ISSET(1, &cpuset) }); // it is important that we reset the cpu mask now for future tests @@ -157,8 +152,7 @@ fn set_custom_cpu_mask() { unsafe { libc::CPU_SET(i, &mut cpuset) }; } - let err = unsafe { sched_setaffinity(PID, size_of::(), &cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_setaffinity(PID, size_of::(), &cpuset) }); } fn parent_child() { @@ -170,15 +164,13 @@ fn parent_child() { let mut parent_cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; unsafe { libc::CPU_SET(0, &mut parent_cpuset) }; - let err = unsafe { sched_setaffinity(PID, size_of::(), &parent_cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_setaffinity(PID, size_of::(), &parent_cpuset) }); std::thread::scope(|spawner| { spawner.spawn(|| { let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut cpuset) }); // the child inherits its parent's set assert!(unsafe { libc::CPU_ISSET(0, &cpuset) }); @@ -189,8 +181,7 @@ fn parent_child() { }); }); - let err = unsafe { sched_getaffinity(PID, size_of::(), &mut parent_cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_getaffinity(PID, size_of::(), &mut parent_cpuset) }); // the parent's set should be unaffected assert!(unsafe { !libc::CPU_ISSET(1, &parent_cpuset) }); @@ -201,8 +192,7 @@ fn parent_child() { unsafe { libc::CPU_SET(i, &mut cpuset) }; } - let err = unsafe { sched_setaffinity(PID, size_of::(), &cpuset) }; - assert_eq!(err, 0); + errno_check(unsafe { sched_setaffinity(PID, size_of::(), &cpuset) }); } fn main() { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index c67386b4f84cf..f9615fc6e4146 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -7,6 +7,8 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::epoll::*; +use libc_utils::*; // This is a set of testcases for blocking epoll. @@ -19,48 +21,20 @@ fn main() { multiple_events_wake_multiple_threads(); } -// Using `as` cast since `EPOLLET` wraps around -const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _; - -#[track_caller] -fn check_epoll_wait( - epfd: i32, - expected_notifications: &[(u32, u64)], - timeout: i32, -) { - let epoll_event = libc::epoll_event { events: 0, u64: 0 }; - let mut array: [libc::epoll_event; N] = [epoll_event; N]; - let maxsize = N; - let array_ptr = array.as_mut_ptr(); - let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) }; - if res < 0 { - panic!("epoll_wait failed: {}", std::io::Error::last_os_error()); - } - let got_notifications = - unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) }; - let got_notifications = got_notifications.iter().map(|e| (e.events, e.u64)).collect::>(); - assert_eq!(got_notifications, expected_notifications, "got wrong notifications"); -} - // This test allows epoll_wait to block, then unblock without notification. fn test_epoll_block_without_notification() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instances. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Register eventfd with epoll. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // epoll_wait to clear notification. - let expected_event = u32::try_from(libc::EPOLLOUT).unwrap(); - let expected_value = fd as u64; - check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0); + check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLOUT, data: fd }], 0); // This epoll wait blocks, and timeout without notification. check_epoll_wait::<1>(epfd, &[], 5); @@ -69,102 +43,74 @@ fn test_epoll_block_without_notification() { // This test triggers notification and unblocks the epoll_wait before timeout. fn test_epoll_block_then_unblock() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Register one side of the socketpair with epoll. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // epoll_wait to clear notification. - let expected_event = u32::try_from(libc::EPOLLOUT).unwrap(); - let expected_value = fds[0] as u64; - check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0); + check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLOUT, data: fds[0] }], 0); // epoll_wait before triggering notification so it will block then get unblocked before timeout. - let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); - let expected_value = fds[0] as u64; let thread1 = thread::spawn(move || { thread::yield_now(); - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[1], b"abcde").unwrap(); }); - check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10); + check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[0] }], 10); thread1.join().unwrap(); } // This test triggers a notification after epoll_wait times out. fn test_notification_after_timeout() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Register one side of the socketpair with epoll. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // epoll_wait to clear notification. - let expected_event = u32::try_from(libc::EPOLLOUT).unwrap(); - let expected_value = fds[0] as u64; - check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0); + check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLOUT, data: fds[0] }], 0); // epoll_wait timeouts without notification. check_epoll_wait::<1>(epfd, &[], 10); // Trigger epoll notification after timeout. - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[1], b"abcde").unwrap(); // Check the result of the notification. - let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); - let expected_value = fds[0] as u64; - check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10); + check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[0] }], 10); } // This test shows a data_race before epoll had vector clocks added. fn test_epoll_race() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instance. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd = unsafe { libc::eventfd(0, flags) }; + let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Register eventfd with the epoll instance. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); static mut VAL: u8 = 0; let thread1 = thread::spawn(move || { // Write to the static mut variable. unsafe { VAL = 1 }; // Write to the eventfd instance. - let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; - // write returns number of bytes written, which is always 8. - assert_eq!(res, 8); + write_all_from_slice(fd, &1_u64.to_ne_bytes()).unwrap(); }); thread::yield_now(); // epoll_wait for the event to happen. - let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); - let expected_value = u64::try_from(fd).unwrap(); - check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1); + check_epoll_wait::<8>(epfd, &[Ev { events: (libc::EPOLLIN | libc::EPOLLOUT), data: fd }], -1); // Read from the static mut variable. #[allow(static_mut_refs)] unsafe { @@ -177,35 +123,29 @@ fn test_epoll_race() { /// epoll it is blocked on. fn wakeup_on_new_interest() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create a socketpair instance. let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Write to fd[0] - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[0], b"abcde").unwrap(); // Block a thread on the epoll instance. let t = std::thread::spawn(move || { - let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); - let expected_value = u64::try_from(fds[1]).unwrap(); - check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)], -1); + check_epoll_wait::<8>( + epfd, + &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[1] }], + -1, + ); }); // Ensure the thread is blocked. std::thread::yield_now(); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP - let mut ev = libc::epoll_event { - events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _, - u64: u64::try_from(fds[1]).unwrap(), - }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) + .unwrap(); // This should wake up the thread. t.join().unwrap(); @@ -215,25 +155,21 @@ fn wakeup_on_new_interest() { /// to consume them all. fn multiple_events_wake_multiple_threads() { // Create an epoll instance. - let epfd = unsafe { libc::epoll_create1(0) }; - assert_ne!(epfd, -1); + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Create an eventfd instance. let flags = libc::EFD_NONBLOCK | libc::EFD_CLOEXEC; - let fd1 = unsafe { libc::eventfd(0, flags) }; + let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Make a duplicate so that we have two file descriptors for the same file description. - let fd2 = unsafe { libc::dup(fd1) }; + let fd2 = errno_result(unsafe { libc::dup(fd1) }).unwrap(); // Register both with epoll. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd1 as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd1, &mut ev) }; - assert_eq!(res, 0); - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd2 as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd2, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd1, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd, fd2, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Consume the initial events. - let expected = [(libc::EPOLLOUT as u32, fd1 as u64), (libc::EPOLLOUT as u32, fd2 as u64)]; + let expected = + [Ev { events: libc::EPOLLOUT, data: fd1 }, Ev { events: libc::EPOLLOUT, data: fd2 }]; check_epoll_wait::<8>(epfd, &expected, -1); // Block two threads on the epoll, both wanting to get just one event. @@ -241,19 +177,19 @@ fn multiple_events_wake_multiple_threads() { let mut e = libc::epoll_event { events: 0, u64: 0 }; let res = unsafe { libc::epoll_wait(epfd, &raw mut e, 1, -1) }; assert!(res == 1); - (e.events, e.u64) + Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() } }); let t2 = thread::spawn(move || { let mut e = libc::epoll_event { events: 0, u64: 0 }; let res = unsafe { libc::epoll_wait(epfd, &raw mut e, 1, -1) }; assert!(res == 1); - (e.events, e.u64) + Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() } }); // Yield so both threads are waiting now. thread::yield_now(); // Trigger the eventfd. This triggers two events at once! - libc_utils::write_all_from_slice(fd1, &0_u64.to_ne_bytes()).unwrap(); + write_all_from_slice(fd1, &0_u64.to_ne_bytes()).unwrap(); // Both threads should have been woken up so that both events can be consumed. let e1 = t1.join().unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index c2789eb2f6c65..63300c9a433c7 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -32,9 +32,6 @@ fn main() { test_issue_4374_reads(); } -// Using `as` cast since `EPOLLET` wraps around -const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _; - #[track_caller] fn check_epoll_wait(epfd: i32, expected_notifications: &[(u32, u64)]) { let epoll_event = libc::epoll_event { events: 0, u64: 0 }; @@ -60,7 +57,7 @@ fn test_epoll_socketpair() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Write to fd[0] - write_all_from_slice(fds[0], "abcde".as_bytes()).unwrap(); + write_all_from_slice(fds[0], b"abcde").unwrap(); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP epoll_ctl_add(epfd, fds[1], EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP).unwrap(); @@ -72,7 +69,7 @@ fn test_epoll_socketpair() { check_epoll_wait_noblock::<8>(epfd, &[]); // Write some more to fd[0]. - write_all_from_slice(fds[0], "abcde".as_bytes()).unwrap(); + write_all_from_slice(fds[0], b"abcde").unwrap(); // This did not change the readiness of fd[1], so we should get no event. // However, Linux seems to always deliver spurious events to the peer on each write, @@ -140,12 +137,15 @@ fn test_epoll_ctl_del() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Write to fd[0] - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() }; + let mut ev = libc::epoll_event { + events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as u32, + u64: u64::try_from(fds[1]).unwrap(), + }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; assert_eq!(res, 0); @@ -168,16 +168,13 @@ fn test_two_epoll_instance() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Write to the socketpair. - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register one side of the socketpair with EPOLLIN | EPOLLOUT | EPOLLET. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fds[1]).unwrap() }; - let res = unsafe { libc::epoll_ctl(epfd1, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; - assert_eq!(res, 0); - let res = unsafe { libc::epoll_ctl(epfd2, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd1, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd2, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Notification should be received from both instance of epoll. let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); @@ -201,20 +198,23 @@ fn test_two_same_fd_in_same_epoll_instance() { assert_ne!(newfd, -1); // Register both fd to the same epoll instance. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: 5 as u64 }; + let mut ev = libc::epoll_event { + events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(), + u64: 5u64, + }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; assert_eq!(res, 0); let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, newfd, &mut ev) }; assert_eq!(res, 0); // Write to the socketpair. - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Two notification should be received. let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); - let expected_value = 5 as u64; + let expected_value = 5u64; check_epoll_wait::<8>( epfd, &[(expected_event, expected_value), (expected_event, expected_value)], @@ -233,9 +233,7 @@ fn test_epoll_eventfd() { let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Check result from epoll_wait. let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); @@ -278,17 +276,13 @@ fn test_epoll_socketpair_both_sides() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Register both fd to the same epoll instance. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[1] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Write to fds[1]. // (We do the write after the register here, unlike in `test_epoll_socketpair`, to ensure // we cover both orders in which this could be done.) - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); @@ -307,7 +301,7 @@ fn test_epoll_socketpair_both_sides() { let res = unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); - assert_eq!(buf, "abcde".as_bytes()); + assert_eq!(buf, *b"abcde"); // The state of fds[1] does not change (was writable, is writable). // However, we force a spurious wakeup as the read buffer just got emptied. @@ -326,9 +320,7 @@ fn test_closed_fd() { let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); @@ -360,9 +352,7 @@ fn test_not_fully_closed_fd() { let newfd = errno_result(unsafe { libc::dup(fd) }).unwrap(); // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: u64::try_from(fd).unwrap() }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Close the original fd that being used to register with epoll. errno_check(unsafe { libc::close(fd) }); @@ -402,7 +392,7 @@ fn test_event_overwrite() { // Register eventfd with EPOLLIN | EPOLLOUT | EPOLLET let mut ev = libc::epoll_event { - events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _, + events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(), u64: u64::try_from(fd).unwrap(), }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; @@ -431,13 +421,13 @@ fn test_socketpair_read() { // Register both fd to the same epoll instance. let mut ev = libc::epoll_event { - events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _, + events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(), u64: fds[0] as u64, }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; assert_eq!(res, 0); let mut ev = libc::epoll_event { - events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _, + events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(), u64: fds[1] as u64, }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; @@ -493,14 +483,14 @@ fn test_no_notification_for_unregister_flag() { // Register fd[0] with EPOLLOUT|EPOLLET. let mut ev = libc::epoll_event { - events: (libc::EPOLLOUT | libc::EPOLLET) as _, + events: (libc::EPOLLOUT | libc::EPOLLET).cast_unsigned(), u64: u64::try_from(fds[0]).unwrap(), }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; assert_eq!(res, 0); // Write to fd[1]. - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res: i32 = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap() }; @@ -534,7 +524,7 @@ fn test_socketpair_epollerr() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Write to fd[0] - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); @@ -543,19 +533,15 @@ fn test_socketpair_epollerr() { errno_check(unsafe { libc::close(fds[1]) }); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP - let mut ev = libc::epoll_event { - events: (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) as _, - u64: u64::try_from(fds[1]).unwrap(), - }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_ne!(res, -1); + epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET | libc::EPOLLRDHUP) + .unwrap(); // Check result from epoll_wait. let expected_event = u32::try_from( libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLHUP | libc::EPOLLRDHUP | libc::EPOLLERR, ) .unwrap(); - let expected_value = u64::try_from(fds[1]).unwrap(); + let expected_value = u64::try_from(fds[0]).unwrap(); check_epoll_wait::<8>(epfd, &[(expected_event, expected_value)]); } @@ -570,12 +556,8 @@ fn test_epoll_lost_events() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Register both fd to the same epoll instance. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[1] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[1], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd, fds[1], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Two notification should be received. But we only provide buffer for one event. let expected_event0 = u32::try_from(libc::EPOLLOUT).unwrap(); @@ -601,12 +583,8 @@ fn test_ready_list_fetching_logic() { let fd1 = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Register both fd to the same epoll instance. At this point, both of them are on the ready list. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd0 as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd0, &mut ev) }; - assert_eq!(res, 0); - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fd1 as u64 }; - let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd1, &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd, fd0, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd, fd1, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Close fd0 so the first entry in the ready list will be empty. errno_check(unsafe { libc::close(fd0) }); @@ -643,9 +621,7 @@ fn test_epoll_ctl_notification() { errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Register one side of the socketpair with epoll. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd0, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd0, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // epoll_wait to clear notification for epfd0. let expected_event = u32::try_from(libc::EPOLLOUT).unwrap(); @@ -657,9 +633,7 @@ fn test_epoll_ctl_notification() { assert_ne!(epfd1, -1); // Register the same file description for epfd1. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd1, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd1, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); check_epoll_wait::<1>(epfd1, &[(expected_event, expected_value)]); // Previously this epoll_wait will receive a notification, but we shouldn't return notification @@ -683,7 +657,7 @@ fn test_issue_3858() { // Register eventfd with EPOLLIN | EPOLLET. let mut ev = libc::epoll_event { - events: (libc::EPOLLIN | libc::EPOLLET) as _, + events: (libc::EPOLLIN | libc::EPOLLET).cast_unsigned(), u64: u64::try_from(fd).unwrap(), }; let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fd, &mut ev) }; @@ -715,9 +689,7 @@ fn test_issue_4374() { assert_eq!(unsafe { libc::fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK) }, 0); // Register fds[0] with epoll while it is writable (but not readable). - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd0, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd0, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Fill up fds[0] so that it is not writable any more. let zeros = [0u8; 512]; @@ -747,16 +719,14 @@ fn test_issue_4374_reads() { assert_eq!(unsafe { libc::fcntl(fds[1], libc::F_SETFL, libc::O_NONBLOCK) }, 0); // Write to fds[1] so that fds[0] becomes readable. - let data = "abcde".as_bytes().as_ptr(); + let data = b"abcde".as_ptr(); let res: i32 = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap() }; assert_eq!(res, 5); // Register fds[0] with epoll while it is readable. - let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 }; - let res = unsafe { libc::epoll_ctl(epfd0, libc::EPOLL_CTL_ADD, fds[0], &mut ev) }; - assert_eq!(res, 0); + epoll_ctl_add(epfd0, fds[0], libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); // Read fds[0] so it is no longer readable. let mut buf = [0u8; 512]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs index fd7fc801dc280..52a0d978963e8 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs @@ -4,11 +4,14 @@ //@compile-flags: -Zmiri-disable-isolation use std::ffi::CString; -use std::io::{Error, ErrorKind}; +use std::io::ErrorKind; use std::os::unix::ffi::OsStrExt; +#[path = "../../utils/libc.rs"] +mod libc_utils; #[path = "../../utils/mod.rs"] mod utils; +use libc_utils::errno_result; fn main() { test_readlink(); @@ -31,44 +34,48 @@ fn test_readlink() { // 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()) }; + let res = errno_result(unsafe { + libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) + }) + .unwrap(); // 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); + assert_eq!(res, (large_buf.len() - 1) as isize); // 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()) }; + let res = errno_result(unsafe { + libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) + }) + .unwrap(); 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 res = unsafe { + let err = errno_result(unsafe { libc::readlink( c"MIRI_MISSING_FILE_NAME".as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len(), ) - }; - assert_eq!(res, -1); - assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); + }) + .unwrap_err(); + assert_eq!(err.kind(), ErrorKind::NotFound); } fn test_nofollow_symlink() { - let bytes = b"Hello, World!\n"; - let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", bytes); + let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", b"Hello, World!\n"); let symlink_path = utils::prepare("test_nofollow_symlink.txt"); std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - let ret = unsafe { libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) }; - assert_eq!(ret, -1); - let err = Error::last_os_error().raw_os_error().unwrap(); - assert_eq!(err, libc::ELOOP); + let err = errno_result(unsafe { + libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) + }) + .unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::ELOOP)); } 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 8c860b5db7baf..00d5f7d97e281 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -1,4 +1,4 @@ -//@ignore-target: windows # File handling is not implemented yet +//@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation #![feature(io_error_more)] @@ -48,7 +48,6 @@ fn main() { test_nofollow_not_symlink(); #[cfg(target_os = "macos")] test_ioctl(); - test_close_stdout(); } fn test_file_open_unix_allow_two_args() { @@ -580,11 +579,3 @@ fn test_ioctl() { assert_eq!(libc::ioctl(fd, libc::FIOCLEX), 0); } } - -fn test_close_stdout() { - // This is std library UB, but that's not relevant since we're - // only interacting with libc here. - unsafe { - libc::close(1); - } -} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index ce3927ce48ca7..20424fc86dc2b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -9,6 +9,7 @@ use std::thread; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::*; fn main() { test_socketpair(); @@ -21,139 +22,89 @@ fn main() { fn test_socketpair() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Read size == data available in buffer. - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); - let mut buf: [u8; 5] = [0; 5]; - let res = - unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 5); - assert_eq!(buf, "abcde".as_bytes()); + let data = b"abcde"; + write_all_from_slice(fds[0], data).unwrap(); + let buf = read_all_into_array::<5>(fds[1]).unwrap(); + assert_eq!(&buf, data); // Read size > data available in buffer. - let data = "abc".as_bytes(); - let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let data = b"abc"; + write_all_from_slice(fds[0], data).unwrap(); let mut buf2: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) }; - assert!(res > 0 && res <= 3); - let res = res as usize; - assert_eq!(buf2[..res], data[..res]); - if res < 3 { - // Drain the rest from the read end. - let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) }; - assert!(res > 0); - } + let (read, rest) = read_into_slice(fds[1], &mut buf2).unwrap(); + assert_eq!(read[..], data[..read.len()]); + // Write 2 more bytes so we can exactly fill the `rest`. + write_all_from_slice(fds[0], b"12").unwrap(); + read_all_into_slice(fds[1], rest).unwrap(); // Test read and write from another direction. // Read size == data available in buffer. - let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); - let mut buf3: [u8; 5] = [0; 5]; - let res = unsafe { - libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) - }; - assert_eq!(res, 5); - assert_eq!(buf3, "12345".as_bytes()); + let data = b"12345"; + write_all_from_slice(fds[1], data).unwrap(); + let buf3 = read_all_into_array::<5>(fds[0]).unwrap(); + assert_eq!(&buf3, data); // Read size > data available in buffer. - let data = "123".as_bytes(); - let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let data = b"123"; + write_all_from_slice(fds[1], data).unwrap(); let mut buf4: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) }; - assert!(res > 0 && res <= 3); - let res = res as usize; - assert_eq!(buf4[..res], data[..res]); - if res < 3 { - // Drain the rest from the read end. - let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) }; - assert!(res > 0); - } + let (read, rest) = read_into_slice(fds[0], &mut buf4).unwrap(); + assert_eq!(read[..], data[..read.len()]); + // Write 2 more bytes so we can exactly fill the `rest`. + write_all_from_slice(fds[1], b"12").unwrap(); + read_all_into_slice(fds[0], rest).unwrap(); // Test when happens when we close one end, with some data in the buffer. - let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; - assert_eq!(res, 3); - unsafe { libc::close(fds[0]) }; + write_all_from_slice(fds[0], data).unwrap(); + errno_check(unsafe { libc::close(fds[0]) }); // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let res = - unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf[0..3], "123".as_bytes()); - let res = - unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 0); // 0-sized read: EOF. + let (res, _) = read_until_eof_into_slice(fds[1], &mut buf).unwrap(); + assert_eq!(res, data); // Writing the other end should emit EPIPE. - let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) }; - assert_eq!(res, -1); - assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE)); + let err = write_all_from_slice(fds[1], &mut buf).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EPIPE)); } fn test_socketpair_threaded() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let thread1 = thread::spawn(move || { - let mut buf: [u8; 5] = [0; 5]; - let res: i64 = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - .try_into() - .unwrap() - }; - assert_eq!(res, 5); - assert_eq!(buf, "abcde".as_bytes()); + let buf = read_all_into_array::<5>(fds[1]).unwrap(); + assert_eq!(&buf, b"abcde"); }); thread::yield_now(); - let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[0], b"abcde").unwrap(); thread1.join().unwrap(); // Read and write from different direction let thread2 = thread::spawn(move || { thread::yield_now(); - let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + write_all_from_slice(fds[1], b"12345").unwrap(); }); - let mut buf: [u8; 5] = [0; 5]; - let res = - unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 5); - assert_eq!(buf, "12345".as_bytes()); + let buf = read_all_into_array::<5>(fds[0]).unwrap(); + assert_eq!(&buf, b"12345"); thread2.join().unwrap(); } fn test_race() { static mut VAL: u8 = 0; let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let thread1 = thread::spawn(move || { - let mut buf: [u8; 1] = [0; 1]; // write() from the main thread will occur before the read() here // because preemption is disabled and the main thread yields after write(). - let res: i32 = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - .try_into() - .unwrap() - }; - assert_eq!(res, 1); - assert_eq!(buf, "a".as_bytes()); + let buf = read_all_into_array::<1>(fds[1]).unwrap(); + assert_eq!(&buf, b"a"); // The read above establishes a happens-before so it is now safe to access this global variable. unsafe { assert_eq!(VAL, 1) }; }); unsafe { VAL = 1 }; - let data = "a".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 1) }; - assert_eq!(res, 1); + write_all_from_slice(fds[0], b"a").unwrap(); thread::yield_now(); thread1.join().unwrap(); } @@ -161,22 +112,15 @@ fn test_race() { // Test the behaviour of a socketpair getting blocked on read and subsequently unblocked. fn test_blocking_read() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let thread1 = thread::spawn(move || { // Let this thread block on read. - let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - }; - assert_eq!(res, 3); - assert_eq!(&buf, "abc".as_bytes()); + let buf = read_all_into_array::<3>(fds[1]).unwrap(); + assert_eq!(&buf, b"abc"); }); let thread2 = thread::spawn(move || { // Unblock thread1 by doing writing something. - let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + write_all_from_slice(fds[0], b"abc").unwrap(); }); thread1.join().unwrap(); thread2.join().unwrap(); @@ -185,26 +129,17 @@ fn test_blocking_read() { // Test the behaviour of a socketpair getting blocked on write and subsequently unblocked. fn test_blocking_write() { let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); let arr1: [u8; 0x34000] = [1; 0x34000]; // Exhaust the space in the buffer so the subsequent write will block. - let res = - unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, arr1.len()) }; - assert_eq!(res, 0x34000); + write_all_from_slice(fds[0], &arr1).unwrap(); let thread1 = thread::spawn(move || { - let data = "abc".as_bytes().as_ptr(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + write_all_from_slice(fds[0], b"abc").unwrap(); }); let thread2 = thread::spawn(move || { // Unblock thread1 by freeing up some space. - let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { - libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) - }; - assert_eq!(res, 3); + let buf = read_all_into_array::<3>(fds[1]).unwrap(); assert_eq!(buf, [1, 1, 1]); }); thread1.join().unwrap(); @@ -215,30 +150,25 @@ fn test_blocking_write() { fn test_socketpair_setfl_getfl() { // Initialise socketpair fds. let mut fds = [-1, -1]; - let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) }); // Test if both sides have O_RDWR. - let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); - let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR); // Add the O_NONBLOCK flag with F_SETFL. - let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }; - assert_eq!(res, 0); + errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK) }); // Test if the O_NONBLOCK flag is successfully added. - let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR | libc::O_NONBLOCK); + assert_eq!( + errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), + libc::O_RDWR | libc::O_NONBLOCK + ); // The other side remains unchanged. - let res = unsafe { libc::fcntl(fds[1], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[1], libc::F_GETFL) }).unwrap(), libc::O_RDWR); // Test if O_NONBLOCK flag can be unset. - let res = unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) }; - assert_eq!(res, 0); - let res = unsafe { libc::fcntl(fds[0], libc::F_GETFL) }; - assert_eq!(res, libc::O_RDWR); + errno_check(unsafe { libc::fcntl(fds[0], libc::F_SETFL, 0) }); + assert_eq!(errno_result(unsafe { libc::fcntl(fds[0], libc::F_GETFL) }).unwrap(), libc::O_RDWR); } 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 9e9fadfca9e7c..b80fb00255305 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -1,8 +1,13 @@ //@ignore-target: windows # no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation + +#[path = "../../utils/libc.rs"] +mod libc_utils; use std::time::{Duration, Instant}; use std::{env, mem, ptr}; +use libc_utils::errno_check; + fn main() { test_clocks(); test_posix_gettimeofday(); @@ -39,30 +44,23 @@ fn main() { /// Tests whether clock support exists at all fn test_clocks() { let mut tp = mem::MaybeUninit::::uninit(); - let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) }; - assert_eq!(is_error, 0); - let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) }); + errno_check(unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) }); #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "android"))] { - let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) }; - assert_eq!(is_error, 0); - let is_error = - unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) }); + errno_check(unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) }); } #[cfg(target_os = "macos")] { - let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) }); } } fn test_posix_gettimeofday() { let mut tp = mem::MaybeUninit::::uninit(); let tz = ptr::null_mut::(); - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz.cast()) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz.cast()) }); let tv = unsafe { tp.assume_init() }; assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. @@ -334,15 +332,13 @@ fn test_nanosleep() { let start_test_sleep = Instant::now(); let duration_zero = libc::timespec { tv_sec: 0, tv_nsec: 0 }; let remainder = ptr::null_mut::(); - let is_error = unsafe { libc::nanosleep(&duration_zero, remainder) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::nanosleep(&duration_zero, remainder) }); assert!(start_test_sleep.elapsed() < Duration::from_millis(100)); let start_test_sleep = Instant::now(); let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 }; let remainder = ptr::null_mut::(); - let is_error = unsafe { libc::nanosleep(&duration_100_millis, remainder) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::nanosleep(&duration_100_millis, remainder) }); assert!(start_test_sleep.elapsed() > Duration::from_millis(100)); } @@ -371,8 +367,7 @@ mod test_clock_nanosleep { /// Helper function to get the current time for testing relative sleeps fn timespec_now(clock: libc::clockid_t) -> libc::timespec { let mut timespec = mem::MaybeUninit::::uninit(); - let is_error = unsafe { libc::clock_gettime(clock, timespec.as_mut_ptr()) }; - assert_eq!(is_error, 0); + errno_check(unsafe { libc::clock_gettime(clock, timespec.as_mut_ptr()) }); unsafe { timespec.assume_init() } } @@ -380,7 +375,7 @@ mod test_clock_nanosleep { let start_test_sleep = Instant::now(); let before_start = libc::timespec { tv_sec: 0, tv_nsec: 0 }; let remainder = ptr::null_mut::(); - let error = unsafe { + errno_check(unsafe { // this will not sleep since unix time zero is in the past libc::clock_nanosleep( libc::CLOCK_MONOTONIC, @@ -388,22 +383,20 @@ mod test_clock_nanosleep { &before_start, remainder, ) - }; - assert_eq!(error, 0); + }); assert!(start_test_sleep.elapsed() < Duration::from_millis(100)); let start_test_sleep = Instant::now(); let hunderd_millis_after_start = add_100_millis(timespec_now(libc::CLOCK_MONOTONIC)); let remainder = ptr::null_mut::(); - let error = unsafe { + errno_check(unsafe { libc::clock_nanosleep( libc::CLOCK_MONOTONIC, libc::TIMER_ABSTIME, &hunderd_millis_after_start, remainder, ) - }; - assert_eq!(error, 0); + }); assert!(start_test_sleep.elapsed() > Duration::from_millis(100)); } @@ -413,19 +406,17 @@ mod test_clock_nanosleep { let start_test_sleep = Instant::now(); let duration_zero = libc::timespec { tv_sec: 0, tv_nsec: 0 }; let remainder = ptr::null_mut::(); - let error = unsafe { + errno_check(unsafe { libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_zero, remainder) - }; - assert_eq!(error, 0); + }); assert!(start_test_sleep.elapsed() < Duration::from_millis(100)); let start_test_sleep = Instant::now(); let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 }; let remainder = ptr::null_mut::(); - let error = unsafe { + errno_check(unsafe { libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_100_millis, remainder) - }; - assert_eq!(error, 0); + }); assert!(start_test_sleep.elapsed() > Duration::from_millis(100)); } } diff --git a/src/tools/miri/tests/pass/concurrency/miri_thread_join_spawned.rs b/src/tools/miri/tests/pass/concurrency/miri_thread_join_spawned.rs new file mode 100644 index 0000000000000..5b78068b68537 --- /dev/null +++ b/src/tools/miri/tests/pass/concurrency/miri_thread_join_spawned.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +//@compile-flags: -Cpanic=abort +//@ignore-target: windows # no-std not supported on Windows + +#[path = "../../utils/mod.no_std.rs"] +mod utils; + +extern "Rust" fn thread_start(_null: *mut ()) { + unsafe { + utils::miri_spin_loop(); + utils::miri_spin_loop(); + } +} + +#[no_mangle] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let thread_id = utils::miri_thread_spawn(thread_start, core::ptr::null_mut()); + assert_eq!(utils::miri_thread_join(thread_id), true); + } + 0 +} + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} diff --git a/src/tools/miri/tests/pass/miri-alloc.rs b/src/tools/miri/tests/pass/miri-alloc.rs index 20269d8ced039..7469e29a8c961 100644 --- a/src/tools/miri/tests/pass/miri-alloc.rs +++ b/src/tools/miri/tests/pass/miri-alloc.rs @@ -6,17 +6,15 @@ // windows linker section, we can run this test on windows again. //@ignore-target: windows # no-std not supported on Windows -extern "Rust" { - fn miri_alloc(size: usize, align: usize) -> *mut u8; - fn miri_dealloc(ptr: *mut u8, size: usize, align: usize); -} +#[path = "../utils/mod.no_std.rs"] +mod utils; #[no_mangle] fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { unsafe { - let ptr = miri_alloc(123, 1); + let ptr = utils::miri_alloc(123, 1); core::ptr::write_bytes(ptr, 0u8, 123); - miri_dealloc(ptr, 123, 1); + utils::miri_dealloc(ptr, 123, 1); } 0 } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index 42acb6c3fb374..e1e23eda84281 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -1,6 +1,6 @@ // We're testing x86 target specific features //@only-target: x86_64 i686 -//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq,+avx512vnni +//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512bitalg,+avx512vpopcntdq,+avx512vnni #[cfg(target_arch = "x86")] use std::arch::x86::*; @@ -11,12 +11,14 @@ use std::mem::transmute; fn main() { assert!(is_x86_feature_detected!("avx512f")); assert!(is_x86_feature_detected!("avx512vl")); + assert!(is_x86_feature_detected!("avx512bw")); assert!(is_x86_feature_detected!("avx512bitalg")); assert!(is_x86_feature_detected!("avx512vpopcntdq")); assert!(is_x86_feature_detected!("avx512vnni")); unsafe { test_avx512(); + test_avx512bw(); test_avx512bitalg(); test_avx512vpopcntdq(); test_avx512ternarylogic(); @@ -100,6 +102,77 @@ unsafe fn test_avx512() { } test_mm512_maddubs_epi16(); + #[target_feature(enable = "avx512bw")] + unsafe fn test_mm512_madd_epi16() { + // Input pairs + // + // - `i16::MIN * i16::MIN + i16::MIN * i16::MIN`: the 32-bit addition overflows + // - `i16::MAX * i16::MAX + i16::MAX * i16::MAX`: check that widening happens before + // arithmetic + // - `i16::MIN * i16::MAX + i16::MAX * i16::MIN`: check that large negative values are + // handled correctly + // - `3 * 1 + 4 * 2`: A sanity check, the result should be 14. + + #[rustfmt::skip] + let a = _mm512_set_epi16( + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MIN, i16::MAX, + 3, 1, + + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MIN, i16::MAX, + 3, 1, + + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MIN, i16::MAX, + 3, 1, + + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MIN, i16::MAX, + 3, 1, + ); + + #[rustfmt::skip] + let b = _mm512_set_epi16( + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MAX, i16::MIN, + 4, 2, + + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MAX, i16::MIN, + 4, 2, + + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MAX, i16::MIN, + 4, 2, + + i16::MIN, i16::MIN, + i16::MAX, i16::MAX, + i16::MAX, i16::MIN, + 4, 2, + ); + + let r = _mm512_madd_epi16(a, b); + + #[rustfmt::skip] + let e = _mm512_set_epi32( + i32::MIN, 2_147_352_578, -2_147_418_112, 14, + i32::MIN, 2_147_352_578, -2_147_418_112, 14, + i32::MIN, 2_147_352_578, -2_147_418_112, 14, + i32::MIN, 2_147_352_578, -2_147_418_112, 14, + ); + + assert_eq_m512i(r, e); + } + test_mm512_madd_epi16(); + #[target_feature(enable = "avx512f")] unsafe fn test_mm512_permutexvar_epi32() { let a = _mm512_set_epi32( @@ -508,9 +581,133 @@ unsafe fn test_avx512vnni() { test_mm512_dpbusd_epi32(); } +#[target_feature(enable = "avx512bw")] +unsafe fn test_avx512bw() { + #[target_feature(enable = "avx512bw")] + unsafe fn test_mm512_packs_epi16() { + let a = _mm512_set1_epi16(120); + + // Because `packs` instructions do signed saturation, we expect + // that any value over `i8::MAX` will be saturated to `i8::MAX`, and any value + // less than `i8::MIN` will also be saturated to `i8::MIN`. + let b = _mm512_set_epi16( + 200, 200, 200, 200, 200, 200, 200, 200, -200, -200, -200, -200, -200, -200, -200, -200, + 200, 200, 200, 200, 200, 200, 200, 200, -200, -200, -200, -200, -200, -200, -200, -200, + ); + + // The pack* family of instructions in x86 operate in blocks + // of 128-bit lanes, meaning the first 128-bit lane in `a` is converted and written + // then the first 128-bit lane of `b`, followed by the second 128-bit lane in `a`, etc... + // Because we are going from 16-bits to 8-bits our 128-bit block becomes 64-bits in + // the output register. + // This leaves us with 8x 8-bit values interleaved in the final register. + #[rustfmt::skip] + const DST: [i8; 64] = [ + 120, 120, 120, 120, 120, 120, 120, 120, + i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, + 120, 120, 120, 120, 120, 120, 120, 120, + i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, + 120, 120, 120, 120, 120, 120, 120, 120, + i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, i8::MIN, + 120, 120, 120, 120, 120, 120, 120, 120, + i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, i8::MAX, + ]; + let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>()); + assert_eq_m512i(_mm512_packs_epi16(a, b), dst); + } + test_mm512_packs_epi16(); + + #[target_feature(enable = "avx512bw")] + unsafe fn test_mm512_packus_epi16() { + let a = _mm512_set1_epi16(120); + + // Because `packus` instructions do unsigned saturation, we expect + // that any value over `u8::MAX` will be saturated to `u8::MAX`, and any value + // less than `u8::MIN` will also be saturated to `u8::MIN`. + let b = _mm512_set_epi16( + 300, 300, 300, 300, 300, 300, 300, 300, -200, -200, -200, -200, -200, -200, -200, -200, + 300, 300, 300, 300, 300, 300, 300, 300, -200, -200, -200, -200, -200, -200, -200, -200, + ); + + // See `test_mm512_packs_epi16` for an explanation of the output structure. + #[rustfmt::skip] + const DST: [u8; 64] = [ + 120, 120, 120, 120, 120, 120, 120, 120, + u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, + 120, 120, 120, 120, 120, 120, 120, 120, + u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, + 120, 120, 120, 120, 120, 120, 120, 120, + u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, u8::MIN, + 120, 120, 120, 120, 120, 120, 120, 120, + u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, u8::MAX, + ]; + let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>()); + assert_eq_m512i(_mm512_packus_epi16(a, b), dst); + } + test_mm512_packus_epi16(); + + #[target_feature(enable = "avx512bw")] + unsafe fn test_mm512_packs_epi32() { + let a = _mm512_set1_epi32(8_000); + + // Because `packs` instructions do signed saturation, we expect + // that any value over `i16::MAX` will be saturated to `i16::MAX`, and any value + // less than `i16::MIN` will also be saturated to `i16::MIN`. + let b = _mm512_set_epi32( + 50_000, 50_000, 50_000, 50_000, -50_000, -50_000, -50_000, -50_000, 50_000, 50_000, + 50_000, 50_000, -50_000, -50_000, -50_000, -50_000, + ); + + // See `test_mm512_packs_epi16` for an explanation of the output structure. + #[rustfmt::skip] + const DST: [i16; 32] = [ + 8_000, 8_000, 8_000, 8_000, + i16::MIN, i16::MIN, i16::MIN, i16::MIN, + 8_000, 8_000, 8_000, 8_000, + i16::MAX, i16::MAX, i16::MAX, i16::MAX, + 8_000, 8_000, 8_000, 8_000, + i16::MIN, i16::MIN, i16::MIN, i16::MIN, + 8_000, 8_000, 8_000, 8_000, + i16::MAX, i16::MAX, i16::MAX, i16::MAX, + ]; + let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>()); + assert_eq_m512i(_mm512_packs_epi32(a, b), dst); + } + test_mm512_packs_epi32(); + + #[target_feature(enable = "avx512bw")] + unsafe fn test_mm512_packus_epi32() { + let a = _mm512_set1_epi32(8_000); + + // Because `packus` instructions do unsigned saturation, we expect + // that any value over `u16::MAX` will be saturated to `u16::MAX`, and any value + // less than `u16::MIN` will also be saturated to `u16::MIN`. + let b = _mm512_set_epi32( + 80_000, 80_000, 80_000, 80_000, -50_000, -50_000, -50_000, -50_000, 80_000, 80_000, + 80_000, 80_000, -50_000, -50_000, -50_000, -50_000, + ); + + // See `test_mm512_packs_epi16` for an explanation of the output structure. + #[rustfmt::skip] + const DST: [u16; 32] = [ + 8_000, 8_000, 8_000, 8_000, + u16::MIN, u16::MIN, u16::MIN, u16::MIN, + 8_000, 8_000, 8_000, 8_000, + u16::MAX, u16::MAX, u16::MAX, u16::MAX, + 8_000, 8_000, 8_000, 8_000, + u16::MIN, u16::MIN, u16::MIN, u16::MIN, + 8_000, 8_000, 8_000, 8_000, + u16::MAX, u16::MAX, u16::MAX, u16::MAX, + ]; + let dst = _mm512_loadu_si512(DST.as_ptr().cast::<__m512i>()); + assert_eq_m512i(_mm512_packus_epi32(a, b), dst); + } + test_mm512_packus_epi32(); +} + #[track_caller] unsafe fn assert_eq_m512i(a: __m512i, b: __m512i) { - assert_eq!(transmute::<_, [i32; 16]>(a), transmute::<_, [i32; 16]>(b)) + assert_eq!(transmute::<_, [u16; 32]>(a), transmute::<_, [u16; 32]>(b)) } #[track_caller] diff --git a/src/tools/miri/tests/pass/tls/windows-tls.rs b/src/tools/miri/tests/pass/tls/windows-tls.rs index 58131be190378..9c5e8eea4d3f2 100644 --- a/src/tools/miri/tests/pass/tls/windows-tls.rs +++ b/src/tools/miri/tests/pass/tls/windows-tls.rs @@ -5,14 +5,14 @@ use std::ptr; extern "system" { fn TlsAlloc() -> u32; - fn TlsSetValue(key: u32, val: *mut c_void) -> bool; + fn TlsSetValue(key: u32, val: *mut c_void) -> i32; fn TlsGetValue(key: u32) -> *mut c_void; - fn TlsFree(key: u32) -> bool; + fn TlsFree(key: u32) -> i32; } fn main() { let key = unsafe { TlsAlloc() }; - assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) }); + assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) != 0 }); assert_eq!(unsafe { TlsGetValue(key).addr() }, 1); - assert!(unsafe { TlsFree(key) }); + assert!(unsafe { TlsFree(key) != 0 }); } diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs index 0765bacb6bd86..0cc6ad0334c01 100644 --- a/src/tools/miri/tests/utils/libc.rs +++ b/src/tools/miri/tests/utils/libc.rs @@ -40,21 +40,17 @@ pub unsafe fn read_all( return read_so_far as libc::ssize_t; } -/// Try to fill the given slice by reading from `fd`. Error if that many bytes could not be read. +/// Try to fill the given slice by reading from `fd`. Panic if that many bytes could not be read. #[track_caller] -pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> Result<(), libc::ssize_t> { - let res = unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) }; - if res >= 0 { - assert_eq!(res as usize, buf.len()); - Ok(()) - } else { - Err(res) - } +pub fn read_all_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<()> { + let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?; + assert_eq!(res as usize, buf.len()); + Ok(()) } /// Read exactly `N` bytes from `fd`. Error if that many bytes could not be read. #[track_caller] -pub fn read_all_into_array(fd: libc::c_int) -> Result<[u8; N], libc::ssize_t> { +pub fn read_all_into_array(fd: libc::c_int) -> io::Result<[u8; N]> { let mut buf = [0; N]; read_all_into_slice(fd, &mut buf)?; Ok(buf) @@ -63,12 +59,20 @@ pub fn read_all_into_array(fd: libc::c_int) -> Result<[u8; N], l /// Do a single read from `fd` and return the part of the buffer that was written into, /// and the rest. #[track_caller] -pub fn read_into_slice( +pub fn read_into_slice(fd: libc::c_int, buf: &mut [u8]) -> io::Result<(&mut [u8], &mut [u8])> { + let res = errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) })?; + Ok(buf.split_at_mut(res as usize)) +} + +/// Read from `fd` until we get EOF and return the part of the buffer that was written into, +/// and the rest. +#[track_caller] +pub fn read_until_eof_into_slice( fd: libc::c_int, buf: &mut [u8], -) -> Result<(&mut [u8], &mut [u8]), libc::ssize_t> { - let res = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), buf.len()) }; - if res >= 0 { Ok(buf.split_at_mut(res as usize)) } else { Err(res) } +) -> io::Result<(&mut [u8], &mut [u8])> { + let res = errno_result(unsafe { read_all(fd, buf.as_mut_ptr().cast(), buf.len()) })?; + Ok(buf.split_at_mut(res as usize)) } pub unsafe fn write_all( @@ -89,16 +93,12 @@ pub unsafe fn write_all( return written_so_far as libc::ssize_t; } -/// Write the entire `buf` to `fd`. Error if not all bytes could be written. +/// Write the entire `buf` to `fd`. Panic if not all bytes could be written. #[track_caller] -pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> Result<(), libc::ssize_t> { - let res = unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) }; - if res >= 0 { - assert_eq!(res as usize, buf.len()); - Ok(()) - } else { - Err(res) - } +pub fn write_all_from_slice(fd: libc::c_int, buf: &[u8]) -> io::Result<()> { + let res = errno_result(unsafe { write_all(fd, buf.as_ptr().cast(), buf.len()) })?; + assert_eq!(res as usize, buf.len()); + Ok(()) } #[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))] @@ -113,7 +113,7 @@ pub mod epoll { /// The libc epoll_event type doesn't fit to the EPOLLIN etc constants, so we have our /// own type. We also make the data field an int since we typically want to store FDs there. - #[derive(PartialEq, Debug)] + #[derive(PartialEq, Debug, Clone, Copy)] pub struct Ev { pub events: c_int, pub data: c_int, @@ -138,10 +138,10 @@ pub mod epoll { } #[track_caller] - pub fn check_epoll_wait_noblock(epfd: i32, expected: &[Ev]) { + pub fn check_epoll_wait(epfd: i32, expected: &[Ev], timeout: i32) { let mut array: [libc::epoll_event; N] = [libc::epoll_event { events: 0, u64: 0 }; N]; let num = errno_result(unsafe { - libc::epoll_wait(epfd, array.as_mut_ptr(), N.try_into().unwrap(), 0) + libc::epoll_wait(epfd, array.as_mut_ptr(), N.try_into().unwrap(), timeout) }) .expect("epoll_wait returned an error"); let got = &mut array[..num.try_into().unwrap()]; @@ -151,4 +151,9 @@ pub mod epoll { .collect::>(); assert_eq!(got, expected, "got wrong notifications"); } + + #[track_caller] + pub fn check_epoll_wait_noblock(epfd: i32, expected: &[Ev]) { + check_epoll_wait::(epfd, expected, 0); + } } diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index bd01866dc34c8..e9cde20412f49 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -156,6 +156,19 @@ extern "Rust" { /// Blocks the current execution if the argument is false pub fn miri_genmc_assume(condition: bool); + /// Miri-provided extern function to spawn a new thread in the interpreter. + /// + /// Returns the thread id. + /// + /// This is useful when no fundamental way of spawning threads is available, e.g. when using + /// `no_std`. + pub fn miri_thread_spawn(t: extern "Rust" fn(*mut ()), data: *mut ()) -> usize; + + /// Miri-provided extern function to join a thread that was spawned by Miri. + pub fn miri_thread_join(thread_id: usize) -> bool; + /// Indicate to Miri that this thread is busy-waiting in a spin loop. + /// + /// As far as Miri is concerned, this is equivalent to `yield_now`. pub fn miri_spin_loop(); }