From c485d7993542c9f0967a504e6b9bfa318da2022c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 1 Apr 2024 14:42:00 -0400 Subject: [PATCH 001/208] Fix capture analysis for by-move closure bodies --- tests/pass/async-closure-captures.rs | 89 ++++++++++++++++++++++++ tests/pass/async-closure-captures.stdout | 10 +++ 2 files changed, 99 insertions(+) create mode 100644 tests/pass/async-closure-captures.rs create mode 100644 tests/pass/async-closure-captures.stdout diff --git a/tests/pass/async-closure-captures.rs b/tests/pass/async-closure-captures.rs new file mode 100644 index 0000000000..acff4a3833 --- /dev/null +++ b/tests/pass/async-closure-captures.rs @@ -0,0 +1,89 @@ +#![feature(async_closure, noop_waker)] + +use std::future::Future; +use std::pin::pin; +use std::task::*; + +pub fn block_on(fut: impl Future) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +fn main() { + block_on(async_main()); +} + +async fn call(f: &impl async Fn() -> T) -> T { + f().await +} + +async fn call_once(f: impl async FnOnce() -> T) -> T { + f().await +} + +#[derive(Debug)] +#[allow(unused)] +struct Hello(i32); + +async fn async_main() { + // Capture something by-ref + { + let x = Hello(0); + let c = async || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + + let x = &Hello(1); + let c = async || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + } + + // Capture something and consume it (force to `AsyncFnOnce`) + { + let x = Hello(2); + let c = async || { + println!("{x:?}"); + drop(x); + }; + call_once(c).await; + } + + // Capture something with `move`, don't consume it + { + let x = Hello(3); + let c = async move || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + + let x = &Hello(4); + let c = async move || { + println!("{x:?}"); + }; + call(&c).await; + call_once(c).await; + } + + // Capture something with `move`, also consume it (so `AsyncFnOnce`) + { + let x = Hello(5); + let c = async move || { + println!("{x:?}"); + drop(x); + }; + call_once(c).await; + } +} diff --git a/tests/pass/async-closure-captures.stdout b/tests/pass/async-closure-captures.stdout new file mode 100644 index 0000000000..a0db6d236f --- /dev/null +++ b/tests/pass/async-closure-captures.stdout @@ -0,0 +1,10 @@ +Hello(0) +Hello(0) +Hello(1) +Hello(1) +Hello(2) +Hello(3) +Hello(3) +Hello(4) +Hello(4) +Hello(5) From 0c5ffd3100ac8e3563bc7266a4e43e02484e0888 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 2 Apr 2024 11:26:57 -0400 Subject: [PATCH 002/208] Comments, comments, comments --- tests/pass/async-closure-captures.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/pass/async-closure-captures.rs b/tests/pass/async-closure-captures.rs index acff4a3833..3e33de32ef 100644 --- a/tests/pass/async-closure-captures.rs +++ b/tests/pass/async-closure-captures.rs @@ -1,3 +1,5 @@ +// Same as rustc's `tests/ui/async-await/async-closures/captures.rs`, keep in sync + #![feature(async_closure, noop_waker)] use std::future::Future; From 34c6202775156b04dd7256346ec864b4a71c2ea2 Mon Sep 17 00:00:00 2001 From: joboet Date: Wed, 3 Apr 2024 15:17:00 +0200 Subject: [PATCH 003/208] rename `expose_addr` to `expose_provenance` --- src/alloc_addresses/mod.rs | 4 ++-- src/shims/intrinsics/simd.rs | 6 +++--- tests/fail/provenance/ptr_invalid.rs | 2 +- tests/fail/stacked_borrows/exposed_only_ro.rs | 2 +- tests/pass/portable-simd-ptrs.rs | 2 +- tests/pass/ptr_int_from_exposed.rs | 10 +++++----- tests/pass/stacked-borrows/int-to-ptr.rs | 4 ++-- tests/pass/stacked-borrows/unknown-bottom-gc.rs | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 6b8e1510a6..fec39ec2b8 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -18,8 +18,8 @@ use reuse_pool::ReusePool; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProvenanceMode { - /// We support `expose_addr`/`with_exposed_provenance` via "wildcard" provenance. - /// However, we want on `with_exposed_provenance` to alert the user of the precision loss. + /// We support `expose_provenance`/`with_exposed_provenance` via "wildcard" provenance. + /// However, we warn on `with_exposed_provenance` to alert the user of the precision loss. Default, /// Like `Default`, but without the warning. Permissive, diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index 9d268f09ed..a2fc4f0f76 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -514,7 +514,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?; this.write_int(res, &dest)?; } - "cast" | "as" | "cast_ptr" | "expose_addr" | "with_exposed_provenance" => { + "cast" | "as" | "cast_ptr" | "expose_provenance" | "with_exposed_provenance" => { let [op] = check_arg_count(args)?; let (op, op_len) = this.operand_to_simd(op)?; let (dest, dest_len) = this.mplace_to_simd(dest)?; @@ -524,7 +524,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let unsafe_cast = intrinsic_name == "cast"; let safe_cast = intrinsic_name == "as"; let ptr_cast = intrinsic_name == "cast_ptr"; - let expose_cast = intrinsic_name == "expose_addr"; + let expose_cast = intrinsic_name == "expose_provenance"; let from_exposed_cast = intrinsic_name == "with_exposed_provenance"; for i in 0..dest_len { @@ -557,7 +557,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.ptr_to_ptr(&op, dest.layout)?, // Ptr/Int casts (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) if expose_cast => - this.pointer_expose_address_cast(&op, dest.layout)?, + this.pointer_expose_provenance_cast(&op, dest.layout)?, (ty::Int(_) | ty::Uint(_), ty::RawPtr(..)) if from_exposed_cast => this.pointer_with_exposed_provenance_cast(&op, dest.layout)?, // Error otherwise diff --git a/tests/fail/provenance/ptr_invalid.rs b/tests/fail/provenance/ptr_invalid.rs index 730859684a..512473cd89 100644 --- a/tests/fail/provenance/ptr_invalid.rs +++ b/tests/fail/provenance/ptr_invalid.rs @@ -4,6 +4,6 @@ fn main() { let x = 42; let xptr = &x as *const i32; - let xptr_invalid = std::ptr::without_provenance::(xptr.expose_addr()); + let xptr_invalid = std::ptr::without_provenance::(xptr.expose_provenance()); let _val = unsafe { *xptr_invalid }; //~ ERROR: is a dangling pointer } diff --git a/tests/fail/stacked_borrows/exposed_only_ro.rs b/tests/fail/stacked_borrows/exposed_only_ro.rs index aa05649d55..608ab71891 100644 --- a/tests/fail/stacked_borrows/exposed_only_ro.rs +++ b/tests/fail/stacked_borrows/exposed_only_ro.rs @@ -6,7 +6,7 @@ fn main() { let mut x = 0; let _fool = &mut x as *mut i32; // this would have fooled the old untagged pointer logic - let addr = (&x as *const i32).expose_addr(); + let addr = (&x as *const i32).expose_provenance(); let ptr = std::ptr::with_exposed_provenance_mut::(addr); unsafe { *ptr = 0 }; //~ ERROR: /write access using .* no exposed tags have suitable permission in the borrow stack/ } diff --git a/tests/pass/portable-simd-ptrs.rs b/tests/pass/portable-simd-ptrs.rs index 3b2d221bd8..096ec78da1 100644 --- a/tests/pass/portable-simd-ptrs.rs +++ b/tests/pass/portable-simd-ptrs.rs @@ -7,6 +7,6 @@ use std::simd::prelude::*; fn main() { // Pointer casts let _val: Simd<*const u8, 4> = Simd::<*const i32, 4>::splat(ptr::null()).cast(); - let addrs = Simd::<*const i32, 4>::splat(ptr::null()).expose_addr(); + let addrs = Simd::<*const i32, 4>::splat(ptr::null()).expose_provenance(); let _ptrs = Simd::<*const i32, 4>::with_exposed_provenance(addrs); } diff --git a/tests/pass/ptr_int_from_exposed.rs b/tests/pass/ptr_int_from_exposed.rs index 8555de986f..5690d7865b 100644 --- a/tests/pass/ptr_int_from_exposed.rs +++ b/tests/pass/ptr_int_from_exposed.rs @@ -10,7 +10,7 @@ fn ptr_roundtrip_out_of_bounds() { let x: i32 = 3; let x_ptr = &x as *const i32; - let x_usize = x_ptr.wrapping_offset(128).expose_addr(); + let x_usize = x_ptr.wrapping_offset(128).expose_provenance(); let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); assert_eq!(unsafe { *ptr }, 3); @@ -24,8 +24,8 @@ fn ptr_roundtrip_confusion() { let x_ptr = &x as *const i32; let y_ptr = &y as *const i32; - let x_usize = x_ptr.expose_addr(); - let y_usize = y_ptr.expose_addr(); + let x_usize = x_ptr.expose_provenance(); + let y_usize = y_ptr.expose_provenance(); let ptr = ptr::with_exposed_provenance::(y_usize); let ptr = ptr.with_addr(x_usize); @@ -37,7 +37,7 @@ fn ptr_roundtrip_imperfect() { let x: u8 = 3; let x_ptr = &x as *const u8; - let x_usize = x_ptr.expose_addr() + 128; + let x_usize = x_ptr.expose_provenance() + 128; let ptr = ptr::with_exposed_provenance::(x_usize).wrapping_offset(-128); assert_eq!(unsafe { *ptr }, 3); @@ -48,7 +48,7 @@ fn ptr_roundtrip_null() { let x = &42; let x_ptr = x as *const i32; let x_null_ptr = x_ptr.with_addr(0); // addr 0, but still the provenance of x - let null = x_null_ptr.expose_addr(); + let null = x_null_ptr.expose_provenance(); assert_eq!(null, 0); let x_null_ptr_copy = ptr::with_exposed_provenance::(null); // just a roundtrip, so has provenance of x (angelically) diff --git a/tests/pass/stacked-borrows/int-to-ptr.rs b/tests/pass/stacked-borrows/int-to-ptr.rs index 5622bf1865..c89d79b42e 100644 --- a/tests/pass/stacked-borrows/int-to-ptr.rs +++ b/tests/pass/stacked-borrows/int-to-ptr.rs @@ -17,7 +17,7 @@ fn example(variant: bool) { unsafe { fn not_so_innocent(x: &mut u32) -> usize { let x_raw4 = x as *mut u32; - x_raw4.expose_addr() + x_raw4.expose_provenance() } let mut c = 42u32; @@ -26,7 +26,7 @@ fn example(variant: bool) { // stack: [..., Unique(1)] let x_raw2 = x_unique1 as *mut u32; - let x_raw2_addr = x_raw2.expose_addr(); + let x_raw2_addr = x_raw2.expose_provenance(); // stack: [..., Unique(1), SharedRW(2)] let x_unique3 = &mut *x_raw2; diff --git a/tests/pass/stacked-borrows/unknown-bottom-gc.rs b/tests/pass/stacked-borrows/unknown-bottom-gc.rs index 6e177a6e4a..55356814a1 100644 --- a/tests/pass/stacked-borrows/unknown-bottom-gc.rs +++ b/tests/pass/stacked-borrows/unknown-bottom-gc.rs @@ -9,7 +9,7 @@ fn main() { // Expose the allocation and use the exposed pointer, creating an unknown bottom unsafe { - let p: *mut u8 = ptr::with_exposed_provenance::(ptr.expose_addr()) as *mut u8; + let p: *mut u8 = ptr::with_exposed_provenance::(ptr.expose_provenance()) as *mut u8; *p = 1; } From b29145e3e532a42948642aff109a60b10b9637a0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 1 Apr 2024 23:56:59 +0200 Subject: [PATCH 004/208] add 'x.py miri', and make it work for 'library/{core,alloc,std}' --- README.md | 2 ++ cargo-miri/src/phases.rs | 53 ++++++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 26e55b8970..bd6efeac09 100644 --- a/README.md +++ b/README.md @@ -507,6 +507,8 @@ binaries, and as such worth documenting: crate currently being compiled. * `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the value of `RUSTDOC` from before it was overwritten. +* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap + run the standard library tests in Miri. * `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to perform verbose logging. * `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 694720ab21..3f6c484a05 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -3,7 +3,7 @@ use std::env; use std::fs::{self, File}; use std::io::BufReader; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; use rustc_version::VersionMeta; @@ -412,9 +412,25 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // Arguments are treated very differently depending on whether this crate is // for interpretation by Miri, or for use by a build script / proc macro. if target_crate { - // Forward arguments, but remove "link" from "--emit" to make this a check-only build. + // Forward arguments, but patched. let emit_flag = "--emit"; + // This hack helps bootstrap run standard library tests in Miri. The issue is as follows: + // when running `cargo miri test` on libcore, cargo builds a local copy of core and makes it + // a dependency of the integration test crate. This copy duplicates all the lang items, so + // the build fails. (Regular testing avoids this because the sysroot is a literal copy of + // what `cargo build` produces, but since Miri builds its own sysroot this does not work for + // us.) So we need to make it so that the locally built libcore contains all the items from + // `core`, but does not re-define them -- we want to replace the entire crate but a + // re-export of the sysroot crate. We do this by swapping out the source file: if + // `MIRI_REPLACE_LIBRS_IF_NOT_TEST` is set and we are building a `lib.rs` file, and a + // `lib.miri.rs` file exists in the same folder, we build that instead. But crucially we + // only do that for the library, not the unit test crate (which would be runnable) or + // rustdoc (which would have a different `phase`). + let replace_librs = env::var_os("MIRI_REPLACE_LIBRS_IF_NOT_TEST").is_some() + && !runnable_crate + && phase == RustcPhase::Build; while let Some(arg) = args.next() { + // Patch `--emit`: remove "link" from "--emit" to make this a check-only build. if let Some(val) = arg.strip_prefix(emit_flag) { // Patch this argument. First, extract its value. let val = @@ -429,13 +445,36 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { } } cmd.arg(format!("{emit_flag}={}", val.join(","))); - } else if arg == "--extern" { - // Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files: - // https://github.com/rust-lang/miri/issues/1705 + continue; + } + // Patch `--extern` filenames, since Cargo sometimes passes stub `.rlib` files: + // https://github.com/rust-lang/miri/issues/1705 + if arg == "--extern" { forward_patched_extern_arg(&mut args, &mut cmd); - } else { - cmd.arg(arg); + continue; } + // If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a + // `lib.miri.rs` file exists, then build that instead. We only consider relative paths + // as cargo uses those for files in the workspace; dependencies from crates.io get + // absolute paths. + if replace_librs { + let path = Path::new(&arg); + if path.is_relative() + && path.file_name().is_some_and(|f| f == "lib.rs") + && path.is_file() + { + let miri_rs = Path::new(&arg).with_extension("miri.rs"); + if miri_rs.is_file() { + if verbose > 0 { + eprintln!("Performing REPLACE_LIBRS hack: {arg:?} -> {miri_rs:?}"); + } + cmd.arg(miri_rs); + continue; + } + } + } + // Fallback: just propagate the argument. + cmd.arg(arg); } // During setup, patch the panic runtime for `libpanic_abort` (mirroring what bootstrap usually does). From 8b0776fd30a8cc93aa6a1c837578ff288079b165 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 4 Apr 2024 04:55:34 +0000 Subject: [PATCH 005/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 90194ec603..02e2e6459f 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -b688d53a1736c17e49328a706a90829a9937a91a +0accf4ec4c07d23aa86f6a97aeb8797941abc30e From f4b44cab305635af1052fb318c66442ee239c7a1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 2 Apr 2024 08:23:38 +0200 Subject: [PATCH 006/208] adjust frame_in_std to recognize std tests --- src/helpers.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index c12fe0e086..7ab73e1139 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -968,10 +968,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn frame_in_std(&self) -> bool { let this = self.eval_context_ref(); - let Some(start_fn) = this.tcx.lang_items().start_fn() else { - // no_std situations - return false; - }; let frame = this.frame(); // Make an attempt to get at the instance of the function this is inlined from. let instance: Option<_> = try { @@ -982,13 +978,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // Fall back to the instance of the function itself. let instance = instance.unwrap_or(frame.instance); - // Now check if this is in the same crate as start_fn. - // As a special exception we also allow unit tests from - // to call these - // shims. + // Now check the crate it is in. We could try to be clever here and e.g. check if this is + // the same crate as `start_fn`, but that would not work for running std tests in Miri, so + // we'd need some more hacks anyway. So we just check the name of the crate. If someone + // calls their crate `std` then we'll just let them keep the pieces. let frame_crate = this.tcx.def_path(instance.def_id()).krate; - frame_crate == this.tcx.def_path(start_fn).krate - || this.tcx.crate_name(frame_crate).as_str() == "std_miri_test" + let crate_name = this.tcx.crate_name(frame_crate); + let crate_name = crate_name.as_str(); + // On miri-test-libstd, the name of the crate is different. + crate_name == "std" || crate_name == "std_miri_test" } /// Handler that should be called when unsupported functionality is encountered. From b0216c78433a434d5584ad0e8a179f5de48a8381 Mon Sep 17 00:00:00 2001 From: belovdv <70999565+belovdv@users.noreply.github.com> Date: Thu, 4 Apr 2024 22:40:00 +0300 Subject: [PATCH 007/208] remove miri jobserver workaround --- cargo-miri/src/phases.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 694720ab21..8e08eb898c 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -519,13 +519,6 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner // Set missing env vars. We prefer build-time env vars over run-time ones; see // for the kind of issue that fixes. for (name, val) in info.env { - // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time - // the program is being run, that jobserver no longer exists (cargo only runs the jobserver - // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. - // Also see . - if name == "CARGO_MAKEFLAGS" { - continue; - } if let Some(old_val) = env::var_os(&name) { if old_val == val { // This one did not actually change, no need to re-set it. From 4b351475a7d2ac441429c570ac4b6e86c7b37df7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 1 Apr 2024 14:47:12 -0400 Subject: [PATCH 008/208] Actually use the inferred ClosureKind from signature inference in coroutine-closures --- tests/pass/async-closure-captures.rs | 34 ++++++++++++++++++++++++ tests/pass/async-closure-captures.stderr | 31 +++++++++++++++++++++ tests/pass/async-closure-captures.stdout | 10 ------- 3 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 tests/pass/async-closure-captures.stderr delete mode 100644 tests/pass/async-closure-captures.stdout diff --git a/tests/pass/async-closure-captures.rs b/tests/pass/async-closure-captures.rs index 3e33de32ef..cac26bfe14 100644 --- a/tests/pass/async-closure-captures.rs +++ b/tests/pass/async-closure-captures.rs @@ -88,4 +88,38 @@ async fn async_main() { }; call_once(c).await; } + + fn force_fnonce(f: impl async FnOnce() -> T) -> impl async FnOnce() -> T { + f + } + + // Capture something with `move`, but infer to `AsyncFnOnce` + { + let x = Hello(6); + let c = force_fnonce(async move || { + println!("{x:?}"); + }); + call_once(c).await; + + let x = &Hello(7); + let c = force_fnonce(async move || { + println!("{x:?}"); + }); + call_once(c).await; + } + + // Capture something by-ref, but infer to `AsyncFnOnce` + { + let x = Hello(8); + let c = force_fnonce(async || { + println!("{x:?}"); + }); + call_once(c).await; + + let x = &Hello(9); + let c = force_fnonce(async || { + println!("{x:?}"); + }); + call_once(c).await; + } } diff --git a/tests/pass/async-closure-captures.stderr b/tests/pass/async-closure-captures.stderr new file mode 100644 index 0000000000..f1548aadef --- /dev/null +++ b/tests/pass/async-closure-captures.stderr @@ -0,0 +1,31 @@ +error[E0597]: `x` does not live long enough + --> $DIR/async-closure-captures.rs:LL:CC + | +LL | let c = force_fnonce(async move || { + | ____________________________________________- +LL | | println!("{x:?}"); + | | ^ borrowed value does not live long enough +LL | | }); + | | -- + | | || + | | |`x` dropped here while still borrowed + | |_________|borrow later used here + | value captured here by coroutine + +error[E0597]: `x` does not live long enough + --> $DIR/async-closure-captures.rs:LL:CC + | +LL | let c = force_fnonce(async move || { + | ____________________________________________- +LL | | println!("{x:?}"); + | | ^ borrowed value does not live long enough +LL | | }); + | | -- + | | || + | | |`x` dropped here while still borrowed + | |_________|borrow later used here + | value captured here by coroutine + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/pass/async-closure-captures.stdout b/tests/pass/async-closure-captures.stdout deleted file mode 100644 index a0db6d236f..0000000000 --- a/tests/pass/async-closure-captures.stdout +++ /dev/null @@ -1,10 +0,0 @@ -Hello(0) -Hello(0) -Hello(1) -Hello(1) -Hello(2) -Hello(3) -Hello(3) -Hello(4) -Hello(4) -Hello(5) From 35f0eca30546a58d411908c9c6f8d39cb5a4710f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 3 Apr 2024 12:16:17 -0400 Subject: [PATCH 009/208] Force `move` async-closures that are `FnOnce` to make their inner coroutines also `move` --- tests/pass/async-closure-captures.stderr | 31 ------------------------ tests/pass/async-closure-captures.stdout | 14 +++++++++++ 2 files changed, 14 insertions(+), 31 deletions(-) delete mode 100644 tests/pass/async-closure-captures.stderr create mode 100644 tests/pass/async-closure-captures.stdout diff --git a/tests/pass/async-closure-captures.stderr b/tests/pass/async-closure-captures.stderr deleted file mode 100644 index f1548aadef..0000000000 --- a/tests/pass/async-closure-captures.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0597]: `x` does not live long enough - --> $DIR/async-closure-captures.rs:LL:CC - | -LL | let c = force_fnonce(async move || { - | ____________________________________________- -LL | | println!("{x:?}"); - | | ^ borrowed value does not live long enough -LL | | }); - | | -- - | | || - | | |`x` dropped here while still borrowed - | |_________|borrow later used here - | value captured here by coroutine - -error[E0597]: `x` does not live long enough - --> $DIR/async-closure-captures.rs:LL:CC - | -LL | let c = force_fnonce(async move || { - | ____________________________________________- -LL | | println!("{x:?}"); - | | ^ borrowed value does not live long enough -LL | | }); - | | -- - | | || - | | |`x` dropped here while still borrowed - | |_________|borrow later used here - | value captured here by coroutine - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0597`. diff --git a/tests/pass/async-closure-captures.stdout b/tests/pass/async-closure-captures.stdout new file mode 100644 index 0000000000..42a7999b2d --- /dev/null +++ b/tests/pass/async-closure-captures.stdout @@ -0,0 +1,14 @@ +Hello(0) +Hello(0) +Hello(1) +Hello(1) +Hello(2) +Hello(3) +Hello(3) +Hello(4) +Hello(4) +Hello(5) +Hello(6) +Hello(7) +Hello(8) +Hello(9) From f5eae9c25167580b846f18f841a2b660208ee6cd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Apr 2024 08:51:46 +0200 Subject: [PATCH 010/208] miri: go look for the item in all crates of the right name --- src/helpers.rs | 51 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 7ab73e1139..6e320b60ee 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -105,24 +105,41 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option) (path, None) }; - // First find the crate. - let krate = - tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == crate_name)?; - let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX }; - // Then go over the modules. - for &segment in modules { - cur_item = find_children(tcx, cur_item, segment) - .find(|item| tcx.def_kind(item) == DefKind::Mod)?; - } - // Finally, look up the desired item in this module, if any. - match item { - Some((item_name, namespace)) => - Some( - find_children(tcx, cur_item, item_name) - .find(|item| tcx.def_kind(item).ns() == Some(namespace))?, - ), - None => Some(cur_item), + // There may be more than one crate with this name. We try them all. + // (This is particularly relevant when running `std` tests as then there are two `std` crates: + // the one in the sysroot and the one locally built by `cargo test`.) + // FIXME: can we prefer the one from the sysroot? + 'crates: for krate in + tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name) + { + let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX }; + // Go over the modules. + for &segment in modules { + let Some(next_item) = find_children(tcx, cur_item, segment) + .find(|item| tcx.def_kind(item) == DefKind::Mod) + else { + continue 'crates; + }; + cur_item = next_item; + } + // Finally, look up the desired item in this module, if any. + match item { + Some((item_name, namespace)) => { + let Some(item) = find_children(tcx, cur_item, item_name) + .find(|item| tcx.def_kind(item).ns() == Some(namespace)) + else { + continue 'crates; + }; + return Some(item); + } + None => { + // Just return the module. + return Some(cur_item); + } + } } + // Item not found in any of the crates with the right name. + None } /// Convert a softfloat type to its corresponding hostfloat type. From e78640baa007b289913ae0895ea502a7cfd197b2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Apr 2024 13:15:16 +0200 Subject: [PATCH 011/208] linux/foreign_items: reorder things to make more sense, remove duplicate socketpair --- src/shims/unix/foreign_items.rs | 7 +-- src/shims/unix/freebsd/foreign_items.rs | 3 +- src/shims/unix/linux/foreign_items.rs | 69 +++++++++++-------------- 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index b1e1aec588..3a56aa9138 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -6,14 +6,9 @@ use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; +use crate::shims::unix::*; use crate::*; use shims::foreign_items::EmulateForeignItemResult; -use shims::unix::fd::EvalContextExt as _; -use shims::unix::fs::EvalContextExt as _; -use shims::unix::mem::EvalContextExt as _; -use shims::unix::socket::EvalContextExt as _; -use shims::unix::sync::EvalContextExt as _; -use shims::unix::thread::EvalContextExt as _; use shims::unix::freebsd::foreign_items as freebsd; use shims::unix::linux::foreign_items as linux; diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index 6814e0d428..ffb583123d 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -1,10 +1,9 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; +use crate::shims::unix::*; use crate::*; use shims::foreign_items::EmulateForeignItemResult; -use shims::unix::fs::EvalContextExt as _; -use shims::unix::thread::EvalContextExt as _; pub fn is_dyn_sym(_name: &str) -> bool { false diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 2aaec3f5a0..497e8bee70 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -29,34 +29,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. match link_name.as_str() { - // errno - "__errno_location" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let errno_place = this.last_error_place()?; - this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; - } - // File related shims (but also see "syscall" below for statx) "readdir64" => { let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.linux_readdir64(dirp)?; this.write_scalar(result, dest)?; } - "mmap64" => { - let [addr, length, prot, flags, fd, offset] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let offset = this.read_scalar(offset)?.to_i64()?; - let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?; - this.write_scalar(ptr, dest)?; - } - - // Linux-only "sync_file_range" => { let [fd, offset, nbytes, flags] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.sync_file_range(fd, offset, nbytes, flags)?; this.write_scalar(result, dest)?; } + + // epoll, eventfd "epoll_create1" => { let [flag] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.epoll_create1(flag)?; @@ -80,29 +66,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } - "mremap" => { - let [old_address, old_size, new_size, flags] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.mremap(old_address, old_size, new_size, flags)?; - this.write_scalar(ptr, dest)?; - } - "socketpair" => { - let [domain, type_, protocol, sv] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - let result = this.socketpair(domain, type_, protocol, sv)?; - this.write_scalar(result, dest)?; - } - "__libc_current_sigrtmin" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - this.write_scalar(Scalar::from_i32(SIGRTMIN), dest)?; - } - "__libc_current_sigrtmax" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?; - } // Threading "pthread_condattr_setclock" => { @@ -204,6 +167,34 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; getrandom(this, ptr, len, flags, dest)?; } + "mmap64" => { + let [addr, length, prot, flags, fd, offset] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let offset = this.read_scalar(offset)?.to_i64()?; + let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?; + this.write_scalar(ptr, dest)?; + } + "mremap" => { + let [old_address, old_size, new_size, flags] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.mremap(old_address, old_size, new_size, flags)?; + this.write_scalar(ptr, dest)?; + } + "__errno_location" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let errno_place = this.last_error_place()?; + this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; + } + "__libc_current_sigrtmin" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + this.write_scalar(Scalar::from_i32(SIGRTMIN), dest)?; + } + "__libc_current_sigrtmax" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?; + } // 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. From 184384f83bfce38610863eedb5f9f86e574e6350 Mon Sep 17 00:00:00 2001 From: belovdv <70999565+belovdv@users.noreply.github.com> Date: Fri, 5 Apr 2024 19:02:16 +0300 Subject: [PATCH 012/208] Revert "remove miri jobserver workaround" This reverts commit af81ab762888eb04d01e9ad5269df5202d6a38b8. --- cargo-miri/src/phases.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index efa3fb0c77..3f6c484a05 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -558,6 +558,13 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner // Set missing env vars. We prefer build-time env vars over run-time ones; see // for the kind of issue that fixes. for (name, val) in info.env { + // `CARGO_MAKEFLAGS` contains information about how to reach the jobserver, but by the time + // the program is being run, that jobserver no longer exists (cargo only runs the jobserver + // for the build portion of `cargo run`/`cargo test`). Hence we shouldn't forward this. + // Also see . + if name == "CARGO_MAKEFLAGS" { + continue; + } if let Some(old_val) = env::var_os(&name) { if old_val == val { // This one did not actually change, no need to re-set it. From ca445b9e778fdf7b53a886e35e132cac97613c4b Mon Sep 17 00:00:00 2001 From: findseat Date: Sat, 6 Apr 2024 10:35:24 +0800 Subject: [PATCH 013/208] chore: fix some typos Signed-off-by: findseat --- src/borrow_tracker/tree_borrows/diagnostics.rs | 4 ++-- src/borrow_tracker/tree_borrows/tree.rs | 2 +- src/borrow_tracker/tree_borrows/tree/tests.rs | 6 +++--- tests/fail/tree_borrows/spurious_read.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/borrow_tracker/tree_borrows/diagnostics.rs index 2690bc026a..b9f0b5bc17 100644 --- a/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -48,7 +48,7 @@ impl AccessCause { /// Complete data for an event: #[derive(Clone, Debug)] pub struct Event { - /// Transformation of permissions that occured because of this event. + /// Transformation of permissions that occurred because of this event. pub transition: PermTransition, /// Kind of the access that triggered this event. pub access_cause: AccessCause, @@ -58,7 +58,7 @@ pub struct Event { /// `None` means that this is an implicit access to the entire allocation /// (used for the implicit read on protector release). pub access_range: Option, - /// The transition recorded by this event only occured on a subrange of + /// The transition recorded by this event only occurred on a subrange of /// `access_range`: a single access on `access_range` triggers several events, /// each with their own mutually disjoint `transition_range`. No-op transitions /// should not be recorded as events, so the union of all `transition_range` is not diff --git a/src/borrow_tracker/tree_borrows/tree.rs b/src/borrow_tracker/tree_borrows/tree.rs index 0fea78daa8..2470624181 100644 --- a/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/borrow_tracker/tree_borrows/tree.rs @@ -473,7 +473,7 @@ impl Tree { let rperms = { let mut perms = UniValMap::default(); // We manually set it to `Active` on all in-bounds positions. - // We also ensure that it is initalized, so that no `Active` but + // We also ensure that it is initialized, so that no `Active` but // not yet initialized nodes exist. Essentially, we pretend there // was a write that initialized these to `Active`. perms.insert(root_idx, LocationState::new_init(Permission::new_active())); diff --git a/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/borrow_tracker/tree_borrows/tree/tests.rs index f568850d8d..6777f41ac2 100644 --- a/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -75,7 +75,7 @@ fn protected_enforces_noalias() { } } -/// We are going to exhaustively test the possibily of inserting +/// We are going to exhaustively test the possibility of inserting /// a spurious read in some code. /// /// We choose some pointer `x` through which we want a spurious read to be inserted. @@ -270,7 +270,7 @@ mod spurious_read { match self { TestEvent::Access(acc) => write!(f, "{acc}"), // The fields of the `Ret` variants just serve to make them - // impossible to instanciate via the `RetX = NoRet` type; we can + // impossible to instantiate via the `RetX = NoRet` type; we can // always ignore their value. TestEvent::RetX(_) => write!(f, "ret x"), TestEvent::RetY(_) => write!(f, "ret y"), @@ -395,7 +395,7 @@ mod spurious_read { match evt { TestEvent::Access(acc) => self.perform_test_access(acc), // The fields of the `Ret` variants just serve to make them - // impossible to instanciate via the `RetX = NoRet` type; we can + // impossible to instantiate via the `RetX = NoRet` type; we can // always ignore their value. TestEvent::RetX(_) => self.end_protector_x(), TestEvent::RetY(_) => self.end_protector_y(), diff --git a/tests/fail/tree_borrows/spurious_read.rs b/tests/fail/tree_borrows/spurious_read.rs index 3f39dcb4b7..50ef0ceef9 100644 --- a/tests/fail/tree_borrows/spurious_read.rs +++ b/tests/fail/tree_borrows/spurious_read.rs @@ -89,7 +89,7 @@ fn retagx_retagy_retx_writey_rety() { // - retag `y` protected // - (wait for the other thread to return so that there is no foreign protector when we write) // - attempt a write through `y`. - // - (UB should have occured by now, but the next step would be to + // - (UB should have occurred by now, but the next step would be to // remove `y`'s protector) let thread_y = thread::spawn(move || { let b = (2, by); From 526578171e581d21b41aaadbd02dd455732c53e6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Apr 2024 08:06:48 +0200 Subject: [PATCH 014/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 02e2e6459f..6ad8fba723 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -0accf4ec4c07d23aa86f6a97aeb8797941abc30e +23d47dba319331d4418827cfbb8c1af283497d3c From 52a66b2baf2892a8a0c4263601220d538ce97dea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Apr 2024 14:08:20 +0200 Subject: [PATCH 015/208] make 'missing extern static' error consistent with missing shim --- src/machine.rs | 8 ++------ tests/fail/extern_static.stderr | 4 ++-- tests/fail/extern_static_in_const.stderr | 4 ++-- tests/fail/extern_static_wrong_size.rs | 2 +- tests/fail/extern_static_wrong_size.stderr | 4 ++-- ...-miri-3288-ice-symbolic-alignment-extern-static.stderr | 4 ++-- 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 14b7afcc97..ff081328a7 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1066,7 +1066,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { let extern_decl_layout = ecx.tcx.layout_of(ty::ParamEnv::empty().and(def_ty)).unwrap(); if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align { throw_unsup_format!( - "`extern` static `{name}` from crate `{krate}` has been declared \ + "extern static `{link_name}` has been declared as `{krate}::{name}` \ with a size of {decl_size} bytes and alignment of {decl_align} bytes, \ but Miri emulates it via an extern static shim \ with a size of {shim_size} bytes and alignment of {shim_align} bytes", @@ -1080,11 +1080,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { } Ok(ptr) } else { - throw_unsup_format!( - "`extern` static `{name}` from crate `{krate}` is not supported by Miri", - name = ecx.tcx.def_path_str(def_id), - krate = ecx.tcx.crate_name(def_id.krate), - ) + throw_unsup_format!("extern static `{link_name}` is not supported by Miri",) } } diff --git a/tests/fail/extern_static.stderr b/tests/fail/extern_static.stderr index c3de4dadb0..34bb273f04 100644 --- a/tests/fail/extern_static.stderr +++ b/tests/fail/extern_static.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: `extern` static `FOO` from crate `extern_static` is not supported by Miri +error: unsupported operation: extern static `FOO` is not supported by Miri --> $DIR/extern_static.rs:LL:CC | LL | let _val = unsafe { std::ptr::addr_of!(FOO) }; - | ^^^ `extern` static `FOO` from crate `extern_static` is not supported by Miri + | ^^^ extern static `FOO` is not supported by Miri | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: BACKTRACE: diff --git a/tests/fail/extern_static_in_const.stderr b/tests/fail/extern_static_in_const.stderr index 23f775f258..45d1632cce 100644 --- a/tests/fail/extern_static_in_const.stderr +++ b/tests/fail/extern_static_in_const.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: `extern` static `E` from crate `extern_static_in_const` is not supported by Miri +error: unsupported operation: extern static `E` is not supported by Miri --> $DIR/extern_static_in_const.rs:LL:CC | LL | let _val = X; - | ^ `extern` static `E` from crate `extern_static_in_const` is not supported by Miri + | ^ extern static `E` is not supported by Miri | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: BACKTRACE: diff --git a/tests/fail/extern_static_wrong_size.rs b/tests/fail/extern_static_wrong_size.rs index 17061f0e5c..fee3c38c25 100644 --- a/tests/fail/extern_static_wrong_size.rs +++ b/tests/fail/extern_static_wrong_size.rs @@ -6,5 +6,5 @@ extern "C" { } fn main() { - let _val = unsafe { environ }; //~ ERROR: /has been declared with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of [48] bytes and alignment of [48] bytes/ + let _val = unsafe { environ }; //~ ERROR: /with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of [48] bytes and alignment of [48] bytes/ } diff --git a/tests/fail/extern_static_wrong_size.stderr b/tests/fail/extern_static_wrong_size.stderr index c935a548f8..27699d780c 100644 --- a/tests/fail/extern_static_wrong_size.stderr +++ b/tests/fail/extern_static_wrong_size.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: `extern` static `environ` from crate `extern_static_wrong_size` has been declared with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes +error: unsupported operation: extern static `environ` has been declared as `extern_static_wrong_size::environ` with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes --> $DIR/extern_static_wrong_size.rs:LL:CC | LL | let _val = unsafe { environ }; - | ^^^^^^^ `extern` static `environ` from crate `extern_static_wrong_size` has been declared with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes + | ^^^^^^^ extern static `environ` has been declared as `extern_static_wrong_size::environ` with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: BACKTRACE: diff --git a/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr b/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr index a4249d2e88..e5dbe74988 100644 --- a/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr +++ b/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr @@ -1,8 +1,8 @@ -error: unsupported operation: `extern` static `_dispatch_queue_attr_concurrent` from crate `issue_miri_3288_ice_symbolic_alignment_extern_static` is not supported by Miri +error: unsupported operation: extern static `_dispatch_queue_attr_concurrent` is not supported by Miri --> $DIR/issue-miri-3288-ice-symbolic-alignment-extern-static.rs:LL:CC | LL | let _val = *DISPATCH_QUEUE_CONCURRENT; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ `extern` static `_dispatch_queue_attr_concurrent` from crate `issue_miri_3288_ice_symbolic_alignment_extern_static` is not supported by Miri + | ^^^^^^^^^^^^^^^^^^^^^^^^^ extern static `_dispatch_queue_attr_concurrent` is not supported by Miri | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support = note: BACKTRACE: From dd418ac07a54600c699b2ef00e39f4a6761b7be2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Apr 2024 22:01:22 +0200 Subject: [PATCH 016/208] MIRI_REPLACE_LIBRS_IF_NOT_TEST: also apply to crates.io crates --- cargo-miri/src/phases.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 3f6c484a05..04f3b2918b 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -454,15 +454,10 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { continue; } // If the REPLACE_LIBRS hack is enabled and we are building a `lib.rs` file, and a - // `lib.miri.rs` file exists, then build that instead. We only consider relative paths - // as cargo uses those for files in the workspace; dependencies from crates.io get - // absolute paths. + // `lib.miri.rs` file exists, then build that instead. if replace_librs { let path = Path::new(&arg); - if path.is_relative() - && path.file_name().is_some_and(|f| f == "lib.rs") - && path.is_file() - { + if path.file_name().is_some_and(|f| f == "lib.rs") && path.is_file() { let miri_rs = Path::new(&arg).with_extension("miri.rs"); if miri_rs.is_file() { if verbose > 0 { From e72b06ee296350a951715e72782fe76ecd031653 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 25 Mar 2024 11:01:28 +0100 Subject: [PATCH 017/208] set --sysroot outside the driver rather than messing with the arguments passed to the driver --- README.md | 5 ++--- cargo-miri/src/phases.rs | 23 ++++++++++++++++------- miri-script/src/commands.rs | 18 +++++++++++------- src/bin/miri.rs | 19 ------------------- tests/ui.rs | 7 +++++++ 5 files changed, 36 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 60a1c1fa1d..28f5f112e9 100644 --- a/README.md +++ b/README.md @@ -464,10 +464,9 @@ Moreover, Miri recognizes some environment variables: standard library that it will build and use for interpretation. This directory must point to the `library` subdirectory of a `rust-lang/rust` repository checkout. -* `MIRI_SYSROOT` (recognized by `cargo miri` and the Miri driver) indicates the sysroot to use. When +* `MIRI_SYSROOT` (recognized by `cargo miri` and the test suite) indicates the sysroot to use. When using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the - automatically created sysroot. For directly invoking the Miri driver, this variable (or a - `--sysroot` flag) is mandatory. When invoking `cargo miri setup`, this indicates where the sysroot + automatically created sysroot. When invoking `cargo miri setup`, this indicates where the sysroot will be put. * `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 3f6c484a05..482b3dcbdf 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -172,8 +172,6 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // Forward all further arguments (not consumed by `ArgSplitFlagValue`) to cargo. cmd.args(args); - // Let it know where the Miri sysroot lives. - cmd.env("MIRI_SYSROOT", miri_sysroot); // Set `RUSTC_WRAPPER` to ourselves. Cargo will prepend that binary to its usual invocation, // i.e., the first argument is `rustc` -- which is what we use in `main` to distinguish // the two codepaths. (That extra argument is why we prefer this over setting `RUSTC`.) @@ -200,10 +198,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // always applied. However, buggy build scripts (https://github.com/eyre-rs/eyre/issues/84) and // also cargo (https://github.com/rust-lang/cargo/issues/10885) will invoke `rustc` even when // `RUSTC_WRAPPER` is set, bypassing the wrapper. To make sure everything is coherent, we want - // that to be the Miri driver, but acting as rustc, on the target level. (Target, rather than - // host, is needed for cross-interpretation situations.) This is not a perfect emulation of real - // rustc (it might be unable to produce binaries since the sysroot is check-only), but it's as - // close as we can get, and it's good enough for autocfg. + // that to be the Miri driver, but acting as rustc, in host mode. // // In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc // or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that @@ -212,7 +207,10 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host // builds. cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap()); - cmd.env("MIRI_BE_RUSTC", "target"); // we better remember to *unset* this in the other phases! + // In case we get invoked as RUSTC without the wrapper, let's be a host rustc. This makes no + // sense for cross-interpretation situations, but without the wrapper, this will use the host + // sysroot, so asking it to behave like a target build makes even less sense. + cmd.env("MIRI_BE_RUSTC", "host"); // we better remember to *unset* this in the other phases! // Set rustdoc to us as well, so we can run doctests. if let Some(orig_rustdoc) = env::var_os("RUSTDOC") { @@ -220,6 +218,8 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { } cmd.env("RUSTDOC", &cargo_miri_path); + // Forward some crucial information to our own re-invocations. + cmd.env("MIRI_SYSROOT", miri_sysroot); cmd.env("MIRI_LOCAL_CRATES", local_crates(&metadata)); if verbose > 0 { cmd.env("MIRI_VERBOSE", verbose.to_string()); // This makes the other phases verbose. @@ -412,6 +412,9 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // Arguments are treated very differently depending on whether this crate is // for interpretation by Miri, or for use by a build script / proc macro. if target_crate { + // Set the sysroot. + cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); + // Forward arguments, but patched. let emit_flag = "--emit"; // This hack helps bootstrap run standard library tests in Miri. The issue is as follows: @@ -579,6 +582,12 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner cmd.env(name, val); } + if phase != RunnerPhase::Rustdoc { + // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot when invoking + // rustdoc itself, which will forward that flag when invoking rustc (i.e., us), so the flag + // is present in `info.args`. + cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); + } // Forward rustc arguments. // We need to patch "--extern" filenames because we forced a check-only // build without cargo knowing about that: replace `.rlib` suffix by diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 55b3b62819..66707dee5e 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -2,6 +2,7 @@ use std::env; use std::ffi::OsString; use std::io::Write; use std::ops::Not; +use std::path::PathBuf; use std::process; use std::thread; use std::time; @@ -20,10 +21,11 @@ const JOSH_FILTER: &str = const JOSH_PORT: &str = "42042"; impl MiriEnv { - fn build_miri_sysroot(&mut self, quiet: bool) -> Result<()> { - if self.sh.var("MIRI_SYSROOT").is_ok() { + /// Returns the location of the sysroot. + fn build_miri_sysroot(&mut self, quiet: bool) -> Result { + if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { // Sysroot already set, use that. - return Ok(()); + return Ok(miri_sysroot.into()); } let manifest_path = path!(self.miri_dir / "cargo-miri" / "Cargo.toml"); let Self { toolchain, cargo_extra_flags, .. } = &self; @@ -57,8 +59,8 @@ impl MiriEnv { .with_context(|| "`cargo miri setup` failed")?; panic!("`cargo miri setup` didn't fail again the 2nd time?"); }; - self.sh.set_var("MIRI_SYSROOT", output); - Ok(()) + self.sh.set_var("MIRI_SYSROOT", &output); + Ok(output.into()) } } @@ -505,8 +507,10 @@ impl Command { flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` } - // Prepare a sysroot. - e.build_miri_sysroot(/* quiet */ true)?; + // Prepare a sysroot, and add it to the flags. + let miri_sysroot = e.build_miri_sysroot(/* quiet */ true)?; + flags.push("--sysroot".into()); + flags.push(miri_sysroot.into()); // Then run the actual command. Also add MIRIFLAGS. let miri_manifest = path!(e.miri_dir / "Cargo.toml"); diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8fffb91542..67a5bf3d14 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -271,25 +271,6 @@ fn run_compiler( callbacks: &mut (dyn rustc_driver::Callbacks + Send), using_internal_features: std::sync::Arc, ) -> ! { - if target_crate { - // Miri needs a custom sysroot for target crates. - // If no `--sysroot` is given, the `MIRI_SYSROOT` env var is consulted to find where - // that sysroot lives, and that is passed to rustc. - let sysroot_flag = "--sysroot"; - if !args.iter().any(|e| e.starts_with(sysroot_flag)) { - // Using the built-in default here would be plain wrong, so we *require* - // the env var to make sure things make sense. - let miri_sysroot = env::var("MIRI_SYSROOT").unwrap_or_else(|_| { - show_error!( - "Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set" - ) - }); - - args.push(sysroot_flag.to_owned()); - args.push(miri_sysroot); - } - } - // Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building // a "host" crate. That may cause procedural macros (and probably build scripts) to // depend on Miri-only symbols, such as `miri_resolve_frame`: diff --git a/tests/ui.rs b/tests/ui.rs index 7e8d140118..8d985a0520 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -112,6 +112,13 @@ fn run_tests( config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into()))); // Add some flags we always want. + config.program.args.push( + format!( + "--sysroot={}", + env::var("MIRI_SYSROOT").expect("MIRI_SYSROOT must be set to run the ui test suite") + ) + .into(), + ); config.program.args.push("-Dwarnings".into()); config.program.args.push("-Dunused".into()); config.program.args.push("-Ainternal_features".into()); From 8727602106f3b7df3460af8b678cadf77cf16a1c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 26 Mar 2024 17:17:05 +0100 Subject: [PATCH 018/208] readme updates --- README.md | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 28f5f112e9..948f1ee6c6 100644 --- a/README.md +++ b/README.md @@ -451,35 +451,32 @@ Some native rustc `-Z` flags are also very relevant for Miri: * `-Zmir-emit-retag` controls whether `Retag` statements are emitted. Miri enables this per default because it is needed for [Stacked Borrows] and [Tree Borrows]. -Moreover, Miri recognizes some environment variables: +Moreover, Miri recognizes some environment variables (unless noted otherwise, these are supported +by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `MIRI_AUTO_OPS` indicates whether the automatic execution of rustfmt, clippy and toolchain setup should be skipped. If it is set to `no`, they are skipped. This is used to allow automated IDE actions to avoid the auto ops. * `MIRI_LOG`, `MIRI_BACKTRACE` control logging and backtrace printing during Miri executions, also [see "Testing the Miri driver" in `CONTRIBUTING.md`][testing-miri]. -* `MIRIFLAGS` (recognized by `cargo miri` and the test suite) defines extra - flags to be passed to Miri. -* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the - standard library that it will build and use for interpretation. This directory - must point to the `library` subdirectory of a `rust-lang/rust` repository - checkout. -* `MIRI_SYSROOT` (recognized by `cargo miri` and the test suite) indicates the sysroot to use. When - using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the - automatically created sysroot. When invoking `cargo miri setup`, this indicates where the sysroot - will be put. -* `MIRI_TEST_TARGET` (recognized by the test suite and the `./miri` script) indicates which target +* `MIRIFLAGS` defines extra flags to be passed to Miri. +* `MIRI_LIB_SRC` defines the directory where Miri expects the sources of the standard library that + it will build and use for interpretation. This directory must point to the `library` subdirectory + of a `rust-lang/rust` repository checkout. +* `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic + setup -- only set this if you do not want to use the automatically created sysroot. When invoking + `cargo miri setup`, this indicates where the sysroot will be put. +* `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same purpose. -* `MIRI_TEST_THREADS` (recognized by the test suite): set the number of threads to use for running tests. - By default the number of cores is used. -* `MIRI_NO_STD` (recognized by `cargo miri`) makes sure that the target's sysroot is built without - libstd. This allows testing and running no_std programs. - (Miri has a heuristic to detect no-std targets based on the target name; this environment variable - is only needed when that heuristic fails.) -* `RUSTC_BLESS` (recognized by the test suite and `cargo-miri-test/run-test.py`): overwrite all +* `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests. + By default, the number of cores is used. +* `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing + and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target + name; this environment variable is only needed when that heuristic fails.) +* `RUSTC_BLESS` (recognized by `./miri test` and `cargo-miri-test/run-test.py`): overwrite all `stderr` and `stdout` files instead of checking whether the output matches. -* `MIRI_SKIP_UI_CHECKS` (recognized by the test suite): don't check whether the +* `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the `stderr` or `stdout` files match the actual output. The following environment variables are *internal* and must not be used by From a4deacbcf4a5d875cafd017efd1c8fb8bf61bdd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Fri, 12 Apr 2024 14:28:33 +0200 Subject: [PATCH 019/208] De-duplicate SSE2 sll/srl/sra code --- src/shims/x86/mod.rs | 80 +++++++++++++++++++ src/shims/x86/sse2.rs | 176 ++++-------------------------------------- 2 files changed, 97 insertions(+), 159 deletions(-) diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 7b7921219e..2a663d300a 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -468,6 +468,86 @@ fn unary_op_ps<'tcx>( Ok(()) } +enum ShiftOp { + /// Shift left, logically (shift in zeros) -- same as shift left, arithmetically + Left, + /// Shift right, logically (shift in zeros) + RightLogic, + /// Shift right, arithmetically (shift in sign) + RightArith, +} + +/// Shifts each element of `left` by a scalar amount. The shift amount +/// is determined by the lowest 64 bits of `right` (which is a 128-bit vector). +/// +/// For logic shifts, when right is larger than BITS - 1, zero is produced. +/// For arithmetic right-shifts, when right is larger than BITS - 1, the sign +/// bit is copied to remaining bits. +fn shift_simd_by_scalar<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + which: ShiftOp, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = this.operand_to_simd(left)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + // `right` may have a different length, and we only care about its + // lowest 64bit anyway. + + // Get the 64-bit shift operand and convert it to the type expected + // by checked_{shl,shr} (u32). + // It is ok to saturate the value to u32::MAX because any value + // above BITS - 1 will produce the same result. + let shift = u32::try_from(extract_first_u64(this, right)?).unwrap_or(u32::MAX); + + for i in 0..dest_len { + let left = this.read_scalar(&this.project_index(&left, i)?)?; + let dest = this.project_index(&dest, i)?; + + let res = match which { + ShiftOp::Left => { + let left = left.to_uint(dest.layout.size)?; + let res = left.checked_shl(shift).unwrap_or(0); + // `truncate` is needed as left-shift can make the absolute value larger. + Scalar::from_uint(dest.layout.size.truncate(res), dest.layout.size) + } + ShiftOp::RightLogic => { + let left = left.to_uint(dest.layout.size)?; + let res = left.checked_shr(shift).unwrap_or(0); + // No `truncate` needed as right-shift can only make the absolute value smaller. + Scalar::from_uint(res, dest.layout.size) + } + ShiftOp::RightArith => { + let left = left.to_int(dest.layout.size)?; + // On overflow, copy the sign bit to the remaining bits + let res = left.checked_shr(shift).unwrap_or(left >> 127); + // No `truncate` needed as right-shift can only make the absolute value smaller. + Scalar::from_int(res, dest.layout.size) + } + }; + this.write_scalar(res, &dest)?; + } + + Ok(()) +} + +/// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts +/// the first value. +fn extract_first_u64<'tcx>( + this: &crate::MiriInterpCx<'_, 'tcx>, + op: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, u64> { + // Transmute vector to `[u64; 2]` + let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?; + let op = op.transmute(array_layout, this)?; + + // Get the first u64 from the array + this.read_scalar(&this.project_index(&op, 0)?)?.to_u64() +} + // Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. fn round_first<'tcx, F: rustc_apfloat::Float>( diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index eb2cc9d37c..9db30d7ddc 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -1,10 +1,11 @@ use rustc_apfloat::ieee::Double; -use rustc_middle::ty::layout::LayoutOf as _; -use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; -use super::{bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, FloatBinOp}; +use super::{ + bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, shift_simd_by_scalar, + FloatBinOp, ShiftOp, +}; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -109,156 +110,27 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_scalar(Scalar::from_u64(res.into()), &dest)?; } } - // Used to implement the _mm_{sll,srl,sra}_epi16 functions. - // Shifts 16-bit packed integers in left by the amount in right. - // Both operands are vectors of 16-bit integers. However, right is - // interpreted as a single 64-bit integer (remaining bits are ignored). - // For logic shifts, when right is larger than 15, zero is produced. - // For arithmetic shifts, when right is larger than 15, the sign bit + // Used to implement the _mm_{sll,srl,sra}_epi{16,32,64} functions + // (except _mm_sra_epi64, which is not available in SSE2). + // Shifts N-bit packed integers in left by the amount in right. + // Both operands are 128-bit vectors. However, right is interpreted as + // a single 64-bit integer (remaining bits are ignored). + // For logic shifts, when right is larger than N - 1, zero is produced. + // For arithmetic shifts, when right is larger than N - 1, the sign bit // is copied to remaining bits. - "psll.w" | "psrl.w" | "psra.w" => { + "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" + | "psrl.q" => { let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum ShiftOp { - Sll, - Srl, - Sra, - } let which = match unprefixed_name { - "psll.w" => ShiftOp::Sll, - "psrl.w" => ShiftOp::Srl, - "psra.w" => ShiftOp::Sra, + "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, + "psrl.w" | "psrl.d" | "psrl.q" => ShiftOp::RightLogic, + "psra.w" | "psra.d" => ShiftOp::RightArith, _ => unreachable!(), }; - // Get the 64-bit shift operand and convert it to the type expected - // by checked_{shl,shr} (u32). - // It is ok to saturate the value to u32::MAX because any value - // above 15 will produce the same result. - let shift = extract_first_u64(this, &right)?.try_into().unwrap_or(u32::MAX); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u16()?; - let dest = this.project_index(&dest, i)?; - - let res = match which { - ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0), - ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0), - #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] - ShiftOp::Sra => { - // Convert u16 to i16 to use arithmetic shift - let left = left as i16; - // Copy the sign bit to the remaining bits - left.checked_shr(shift).unwrap_or(left >> 15) as u16 - } - }; - - this.write_scalar(Scalar::from_u16(res), &dest)?; - } - } - // Used to implement the _mm_{sll,srl,sra}_epi32 functions. - // 32-bit equivalent to the shift functions above. - "psll.d" | "psrl.d" | "psra.d" => { - let [left, right] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum ShiftOp { - Sll, - Srl, - Sra, - } - let which = match unprefixed_name { - "psll.d" => ShiftOp::Sll, - "psrl.d" => ShiftOp::Srl, - "psra.d" => ShiftOp::Sra, - _ => unreachable!(), - }; - - // Get the 64-bit shift operand and convert it to the type expected - // by checked_{shl,shr} (u32). - // It is ok to saturate the value to u32::MAX because any value - // above 31 will produce the same result. - let shift = extract_first_u64(this, &right)?.try_into().unwrap_or(u32::MAX); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u32()?; - let dest = this.project_index(&dest, i)?; - - let res = match which { - ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0), - ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0), - #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] - ShiftOp::Sra => { - // Convert u32 to i32 to use arithmetic shift - let left = left as i32; - // Copy the sign bit to the remaining bits - left.checked_shr(shift).unwrap_or(left >> 31) as u32 - } - }; - - this.write_scalar(Scalar::from_u32(res), &dest)?; - } - } - // Used to implement the _mm_{sll,srl}_epi64 functions. - // 64-bit equivalent to the shift functions above, except _mm_sra_epi64, - // which is not available in SSE2. - "psll.q" | "psrl.q" => { - let [left, right] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - enum ShiftOp { - Sll, - Srl, - } - let which = match unprefixed_name { - "psll.q" => ShiftOp::Sll, - "psrl.q" => ShiftOp::Srl, - _ => unreachable!(), - }; - - // Get the 64-bit shift operand and convert it to the type expected - // by checked_{shl,shr} (u32). - // It is ok to saturate the value to u32::MAX because any value - // above 63 will produce the same result. - let shift = this - .read_scalar(&this.project_index(&right, 0)?)? - .to_u64()? - .try_into() - .unwrap_or(u32::MAX); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_u64()?; - let dest = this.project_index(&dest, i)?; - - let res = match which { - ShiftOp::Sll => left.checked_shl(shift).unwrap_or(0), - ShiftOp::Srl => left.checked_shr(shift).unwrap_or(0), - }; - - this.write_scalar(Scalar::from_u64(res), &dest)?; - } + shift_simd_by_scalar(this, left, right, which, dest)?; } // Used to implement the _mm_cvtps_epi32, _mm_cvttps_epi32, _mm_cvtpd_epi32 // and _mm_cvttpd_epi32 functions. @@ -585,17 +457,3 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: Ok(EmulateForeignItemResult::NeedsJumping) } } - -/// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts -/// the first value. -fn extract_first_u64<'tcx>( - this: &crate::MiriInterpCx<'_, 'tcx>, - op: &MPlaceTy<'tcx, Provenance>, -) -> InterpResult<'tcx, u64> { - // Transmute vector to `[u64; 2]` - let u64_array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?; - let op = op.transmute(u64_array_layout, this)?; - - // Get the first u64 from the array - this.read_scalar(&this.project_index(&op, 0)?)?.to_u64() -} From bf63152988dffb655d05bdebd89c1a8d739217f6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 23 Feb 2024 20:13:51 +0000 Subject: [PATCH 020/208] Run static initializers --- src/bin/miri.rs | 42 +++++++++++++-------- src/shims/foreign_items.rs | 75 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 15 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 8fffb91542..c75d1b0cc6 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -32,8 +32,9 @@ use rustc_driver::Compilation; use rustc_hir::{self as hir, Node}; use rustc_interface::interface::Config; use rustc_middle::{ - middle::exported_symbols::{ - ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, + middle::{ + codegen_fn_attrs::CodegenFnAttrFlags, + exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel}, }, query::LocalCrate, ty::TyCtxt, @@ -136,6 +137,8 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { config.override_queries = Some(|_, local_providers| { // `exported_symbols` and `reachable_non_generics` provided by rustc always returns // an empty result if `tcx.sess.opts.output_types.should_codegen()` is false. + // In addition we need to add #[used] symbols to exported_symbols for .init_array + // handling. local_providers.exported_symbols = |tcx, LocalCrate| { let reachable_set = tcx.with_stable_hashing_context(|hcx| { tcx.reachable_set(()).to_sorted(&hcx, true) @@ -160,19 +163,28 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { }) if !tcx.generics_of(local_def_id).requires_monomorphization(tcx) ); - (is_reachable_non_generic - && tcx.codegen_fn_attrs(local_def_id).contains_extern_indicator()) - .then_some(( - ExportedSymbol::NonGeneric(local_def_id.to_def_id()), - // Some dummy `SymbolExportInfo` here. We only use - // `exported_symbols` in shims/foreign_items.rs and the export info - // is ignored. - SymbolExportInfo { - level: SymbolExportLevel::C, - kind: SymbolExportKind::Text, - used: false, - }, - )) + if !is_reachable_non_generic { + return None; + } + let codegen_fn_attrs = tcx.codegen_fn_attrs(local_def_id); + if codegen_fn_attrs.contains_extern_indicator() + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED) + || codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) + { + Some(( + ExportedSymbol::NonGeneric(local_def_id.to_def_id()), + // Some dummy `SymbolExportInfo` here. We only use + // `exported_symbols` in shims/foreign_items.rs and the export info + // is ignored. + SymbolExportInfo { + level: SymbolExportLevel::C, + kind: SymbolExportKind::Text, + used: false, + }, + )) + } else { + None + } }), ) } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index a25d377f3a..5498d9b83f 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -158,6 +158,81 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } + fn lookup_init_array(&mut self) -> InterpResult<'tcx, Vec>> { + let this = self.eval_context_mut(); + let tcx = this.tcx.tcx; + + let mut init_arrays = vec![]; + + let dependency_formats = tcx.dependency_formats(()); + let dependency_format = dependency_formats + .iter() + .find(|(crate_type, _)| *crate_type == CrateType::Executable) + .expect("interpreting a non-executable crate"); + for cnum in iter::once(LOCAL_CRATE).chain( + dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| { + // We add 1 to the number because that's what rustc also does everywhere it + // calls `CrateNum::new`... + #[allow(clippy::arithmetic_side_effects)] + (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)) + }), + ) { + for &(symbol, _export_info) in tcx.exported_symbols(cnum) { + if let ExportedSymbol::NonGeneric(def_id) = symbol { + let attrs = tcx.codegen_fn_attrs(def_id); + let link_section = if let Some(link_section) = attrs.link_section { + if !link_section.as_str().starts_with(".init_array") { + continue; + } + link_section + } else { + continue; + }; + + init_arrays.push((link_section, def_id)); + } + } + } + + init_arrays.sort_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str())); + + let endianness = tcx.data_layout.endian; + let ptr_size = tcx.data_layout.pointer_size; + + let mut init_array = vec![]; + + for (_, def_id) in init_arrays { + let alloc = tcx.eval_static_initializer(def_id)?.inner(); + let mut expected_offset = Size::ZERO; + for &(offset, prov) in alloc.provenance().ptrs().iter() { + if offset != expected_offset { + throw_ub_format!(".init_array.* may not contain any non-function pointer data"); + } + expected_offset += ptr_size; + + let alloc_id = prov.alloc_id(); + + let reloc_target_alloc = tcx.global_alloc(alloc_id); + match reloc_target_alloc { + GlobalAlloc::Function(instance) => { + let addend = { + let offset = offset.bytes() as usize; + let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter( + offset..offset + ptr_size.bytes() as usize, + ); + read_target_uint(endianness, bytes).unwrap() + }; + assert_eq!(addend, 0); + init_array.push(instance); + } + _ => throw_ub_format!(".init_array.* member is not a function pointer"), + } + } + } + + Ok(init_array) + } + /// Lookup the body of a function that has `link_name` as the symbol name. fn lookup_exported_symbol( &mut self, From d465afb4435481773d9538957226996e945191fe Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 14 Apr 2024 18:56:31 +0200 Subject: [PATCH 021/208] Miri: run .CRT$XLB linker section on thread-end --- src/bin/miri.rs | 3 +- src/concurrency/thread.rs | 2 +- src/eval.rs | 8 +- src/helpers.rs | 74 ++++++++++++++- src/shims/foreign_items.rs | 188 ++++++++----------------------------- src/shims/tls.rs | 112 ++++++++++++---------- 6 files changed, 179 insertions(+), 208 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index c75d1b0cc6..64e2c13a45 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -137,8 +137,7 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { config.override_queries = Some(|_, local_providers| { // `exported_symbols` and `reachable_non_generics` provided by rustc always returns // an empty result if `tcx.sess.opts.output_types.should_codegen()` is false. - // In addition we need to add #[used] symbols to exported_symbols for .init_array - // handling. + // In addition we need to add #[used] symbols to exported_symbols for `lookup_link_section`. local_providers.exported_symbols = |tcx, LocalCrate| { let reachable_set = tcx.with_stable_hashing_context(|hcx| { tcx.reachable_set(()).to_sorted(&hcx, true) diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index d0d73bb1b3..d1136272f0 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -158,7 +158,7 @@ pub struct Thread<'mir, 'tcx> { } pub type StackEmptyCallback<'mir, 'tcx> = - Box) -> InterpResult<'tcx, Poll<()>>>; + Box) -> InterpResult<'tcx, Poll<()>> + 'tcx>; impl<'mir, 'tcx> Thread<'mir, 'tcx> { /// Get the name of the current thread if it was set. diff --git a/src/eval.rs b/src/eval.rs index f7edbdb9c5..df0ede1e1b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -192,18 +192,18 @@ impl Default for MiriConfig { /// The state of the main thread. Implementation detail of `on_main_stack_empty`. #[derive(Default, Debug)] -enum MainThreadState { +enum MainThreadState<'tcx> { #[default] Running, - TlsDtors(tls::TlsDtorsState), + TlsDtors(tls::TlsDtorsState<'tcx>), Yield { remaining: u32, }, Done, } -impl MainThreadState { - fn on_main_stack_empty<'tcx>( +impl<'tcx> MainThreadState<'tcx> { + fn on_main_stack_empty( &mut self, this: &mut MiriInterpCx<'_, 'tcx>, ) -> InterpResult<'tcx, Poll<()>> { diff --git a/src/helpers.rs b/src/helpers.rs index 6e320b60ee..55be4a4379 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -9,16 +9,21 @@ use rand::RngCore; use rustc_apfloat::ieee::{Double, Single}; use rustc_apfloat::Float; -use rustc_hir::def::{DefKind, Namespace}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc_hir::{ + def::{DefKind, Namespace}, + def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}, +}; use rustc_index::IndexVec; +use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir; use rustc_middle::ty::{ self, layout::{LayoutOf, TyAndLayout}, FloatTy, IntTy, Ty, TyCtxt, UintTy, }; -use rustc_span::{def_id::CrateNum, sym, Span, Symbol}; +use rustc_session::config::CrateType; +use rustc_span::{sym, Span, Symbol}; use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants}; use rustc_target::spec::abi::Abi; @@ -142,6 +147,38 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option) None } +/// Call `f` for each exported symbol. +pub fn iter_exported_symbols<'tcx>( + tcx: TyCtxt<'tcx>, + mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>, +) -> InterpResult<'tcx> { + // `dependency_formats` includes all the transitive informations needed to link a crate, + // which is what we need here since we need to dig out `exported_symbols` from all transitive + // dependencies. + let dependency_formats = tcx.dependency_formats(()); + let dependency_format = dependency_formats + .iter() + .find(|(crate_type, _)| *crate_type == CrateType::Executable) + .expect("interpreting a non-executable crate"); + for cnum in iter::once(LOCAL_CRATE).chain(dependency_format.1.iter().enumerate().filter_map( + |(num, &linkage)| { + // We add 1 to the number because that's what rustc also does everywhere it + // calls `CrateNum::new`... + #[allow(clippy::arithmetic_side_effects)] + (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)) + }, + )) { + // We can ignore `_export_info` here: we are a Rust crate, and everything is exported + // from a Rust crate. + for &(symbol, _export_info) in tcx.exported_symbols(cnum) { + if let ExportedSymbol::NonGeneric(def_id) = symbol { + f(cnum, def_id)?; + } + } + } + Ok(()) +} + /// Convert a softfloat type to its corresponding hostfloat type. pub trait ToHost { type HostFloat; @@ -1180,6 +1217,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } Ok(()) } + + /// Lookup an array of immediates stored as a linker section of name `name`. + fn lookup_link_section( + &mut self, + name: &str, + ) -> InterpResult<'tcx, Vec>> { + let this = self.eval_context_mut(); + let tcx = this.tcx.tcx; + + let mut array = vec![]; + + iter_exported_symbols(tcx, |_cnum, def_id| { + let attrs = tcx.codegen_fn_attrs(def_id); + let Some(link_section) = attrs.link_section else { + return Ok(()); + }; + if link_section.as_str() == name { + let instance = ty::Instance::mono(tcx, def_id); + let const_val = this.eval_global(instance).unwrap_or_else(|err| { + panic!( + "failed to evaluate static in required link_section: {def_id:?}\n{err:?}" + ) + }); + let val = this.read_immediate(&const_val)?; + array.push(val); + } + Ok(()) + })?; + + Ok(array) + } } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 5498d9b83f..6b0797f6da 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -2,17 +2,10 @@ use std::{collections::hash_map::Entry, io::Write, iter, path::Path}; use rustc_apfloat::Float; use rustc_ast::expand::allocator::AllocatorKind; -use rustc_hir::{ - def::DefKind, - def_id::{CrateNum, LOCAL_CRATE}, -}; -use rustc_middle::middle::{ - codegen_fn_attrs::CodegenFnAttrFlags, dependency_format::Linkage, - exported_symbols::ExportedSymbol, -}; +use rustc_hir::{def::DefKind, def_id::CrateNum}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; use rustc_middle::ty; -use rustc_session::config::CrateType; use rustc_span::Symbol; use rustc_target::{ abi::{Align, Size}, @@ -158,81 +151,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(()) } - fn lookup_init_array(&mut self) -> InterpResult<'tcx, Vec>> { - let this = self.eval_context_mut(); - let tcx = this.tcx.tcx; - - let mut init_arrays = vec![]; - - let dependency_formats = tcx.dependency_formats(()); - let dependency_format = dependency_formats - .iter() - .find(|(crate_type, _)| *crate_type == CrateType::Executable) - .expect("interpreting a non-executable crate"); - for cnum in iter::once(LOCAL_CRATE).chain( - dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| { - // We add 1 to the number because that's what rustc also does everywhere it - // calls `CrateNum::new`... - #[allow(clippy::arithmetic_side_effects)] - (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)) - }), - ) { - for &(symbol, _export_info) in tcx.exported_symbols(cnum) { - if let ExportedSymbol::NonGeneric(def_id) = symbol { - let attrs = tcx.codegen_fn_attrs(def_id); - let link_section = if let Some(link_section) = attrs.link_section { - if !link_section.as_str().starts_with(".init_array") { - continue; - } - link_section - } else { - continue; - }; - - init_arrays.push((link_section, def_id)); - } - } - } - - init_arrays.sort_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str())); - - let endianness = tcx.data_layout.endian; - let ptr_size = tcx.data_layout.pointer_size; - - let mut init_array = vec![]; - - for (_, def_id) in init_arrays { - let alloc = tcx.eval_static_initializer(def_id)?.inner(); - let mut expected_offset = Size::ZERO; - for &(offset, prov) in alloc.provenance().ptrs().iter() { - if offset != expected_offset { - throw_ub_format!(".init_array.* may not contain any non-function pointer data"); - } - expected_offset += ptr_size; - - let alloc_id = prov.alloc_id(); - - let reloc_target_alloc = tcx.global_alloc(alloc_id); - match reloc_target_alloc { - GlobalAlloc::Function(instance) => { - let addend = { - let offset = offset.bytes() as usize; - let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter( - offset..offset + ptr_size.bytes() as usize, - ); - read_target_uint(endianness, bytes).unwrap() - }; - assert_eq!(addend, 0); - init_array.push(instance); - } - _ => throw_ub_format!(".init_array.* member is not a function pointer"), - } - } - } - - Ok(init_array) - } - /// Lookup the body of a function that has `link_name` as the symbol name. fn lookup_exported_symbol( &mut self, @@ -249,74 +167,48 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Entry::Vacant(e) => { // Find it if it was not cached. let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None; - // `dependency_formats` includes all the transitive informations needed to link a crate, - // which is what we need here since we need to dig out `exported_symbols` from all transitive - // dependencies. - let dependency_formats = tcx.dependency_formats(()); - let dependency_format = dependency_formats - .iter() - .find(|(crate_type, _)| *crate_type == CrateType::Executable) - .expect("interpreting a non-executable crate"); - for cnum in iter::once(LOCAL_CRATE).chain( - dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| { - // We add 1 to the number because that's what rustc also does everywhere it - // calls `CrateNum::new`... - #[allow(clippy::arithmetic_side_effects)] - (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1)) - }), - ) { - // We can ignore `_export_info` here: we are a Rust crate, and everything is exported - // from a Rust crate. - for &(symbol, _export_info) in tcx.exported_symbols(cnum) { - if let ExportedSymbol::NonGeneric(def_id) = symbol { - let attrs = tcx.codegen_fn_attrs(def_id); - let symbol_name = if let Some(export_name) = attrs.export_name { - export_name - } else if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { - tcx.item_name(def_id) + helpers::iter_exported_symbols(tcx, |cnum, def_id| { + let attrs = tcx.codegen_fn_attrs(def_id); + let symbol_name = if let Some(export_name) = attrs.export_name { + export_name + } else if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) { + tcx.item_name(def_id) + } else { + // Skip over items without an explicitly defined symbol name. + return Ok(()); + }; + if symbol_name == link_name { + if let Some((original_instance, original_cnum)) = instance_and_crate { + // Make sure we are consistent wrt what is 'first' and 'second'. + let original_span = tcx.def_span(original_instance.def_id()).data(); + let span = tcx.def_span(def_id).data(); + if original_span < span { + throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions { + link_name, + first: original_span, + first_crate: tcx.crate_name(original_cnum), + second: span, + second_crate: tcx.crate_name(cnum), + }); } else { - // Skip over items without an explicitly defined symbol name. - continue; - }; - if symbol_name == link_name { - if let Some((original_instance, original_cnum)) = instance_and_crate - { - // Make sure we are consistent wrt what is 'first' and 'second'. - let original_span = - tcx.def_span(original_instance.def_id()).data(); - let span = tcx.def_span(def_id).data(); - if original_span < span { - throw_machine_stop!( - TerminationInfo::MultipleSymbolDefinitions { - link_name, - first: original_span, - first_crate: tcx.crate_name(original_cnum), - second: span, - second_crate: tcx.crate_name(cnum), - } - ); - } else { - throw_machine_stop!( - TerminationInfo::MultipleSymbolDefinitions { - link_name, - first: span, - first_crate: tcx.crate_name(cnum), - second: original_span, - second_crate: tcx.crate_name(original_cnum), - } - ); - } - } - if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { - throw_ub_format!( - "attempt to call an exported symbol that is not defined as a function" - ); - } - instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum)); + throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions { + link_name, + first: span, + first_crate: tcx.crate_name(cnum), + second: original_span, + second_crate: tcx.crate_name(original_cnum), + }); } } + if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) { + throw_ub_format!( + "attempt to call an exported symbol that is not defined as a function" + ); + } + instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum)); } - } + Ok(()) + })?; e.insert(instance_and_crate.map(|ic| ic.0)) } diff --git a/src/shims/tls.rs b/src/shims/tls.rs index 7f929d9a91..d25bae1cdc 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -219,61 +219,76 @@ impl VisitProvenance for TlsData<'_> { } #[derive(Debug, Default)] -pub struct TlsDtorsState(TlsDtorsStatePriv); +pub struct TlsDtorsState<'tcx>(TlsDtorsStatePriv<'tcx>); #[derive(Debug, Default)] -enum TlsDtorsStatePriv { +enum TlsDtorsStatePriv<'tcx> { #[default] Init, PthreadDtors(RunningDtorState), + /// For Windows Dtors, we store the list of functions that we still have to call. + /// These are functions from the magic `.CRT$XLB` linker section. + WindowsDtors(Vec>), Done, } -impl TlsDtorsState { - pub fn on_stack_empty<'tcx>( +impl<'tcx> TlsDtorsState<'tcx> { + pub fn on_stack_empty( &mut self, this: &mut MiriInterpCx<'_, 'tcx>, ) -> InterpResult<'tcx, Poll<()>> { use TlsDtorsStatePriv::*; - match &mut self.0 { - Init => { - match this.tcx.sess.target.os.as_ref() { - "linux" | "freebsd" | "android" => { - // Run the pthread dtors. - self.0 = PthreadDtors(Default::default()); + let new_state = 'new_state: { + match &mut self.0 { + Init => { + match this.tcx.sess.target.os.as_ref() { + "linux" | "freebsd" | "android" => { + // Run the pthread dtors. + break 'new_state PthreadDtors(Default::default()); + } + "macos" => { + // The macOS thread wide destructor runs "before any TLS slots get + // freed", so do that first. + this.schedule_macos_tls_dtor()?; + // When the stack is empty again, go on with the pthread dtors. + break 'new_state PthreadDtors(Default::default()); + } + "windows" => { + // Determine which destructors to run. + let dtors = this.lookup_windows_tls_dtors()?; + // And move to the final state. + break 'new_state WindowsDtors(dtors); + } + _ => { + // No TLS dtor support. + // FIXME: should we do something on wasi? + break 'new_state Done; + } } - "macos" => { - // The macOS thread wide destructor runs "before any TLS slots get - // freed", so do that first. - this.schedule_macos_tls_dtor()?; - // When the stack is empty again, go on with the pthread dtors. - self.0 = PthreadDtors(Default::default()); - } - "windows" => { - // Run the special magic hook. - this.schedule_windows_tls_dtors()?; - // And move to the final state. - self.0 = Done; + } + PthreadDtors(state) => { + match this.schedule_next_pthread_tls_dtor(state)? { + Poll::Pending => return Ok(Poll::Pending), // just keep going + Poll::Ready(()) => break 'new_state Done, } - _ => { - // No TLS dtor support. - // FIXME: should we do something on wasi? - self.0 = Done; + } + WindowsDtors(dtors) => { + if let Some(dtor) = dtors.pop() { + this.schedule_windows_tls_dtor(dtor)?; + return Ok(Poll::Pending); // we stay in this state (but `dtors` got shorter) + } else { + // No more destructors to run. + break 'new_state Done; } } - } - PthreadDtors(state) => { - match this.schedule_next_pthread_tls_dtor(state)? { - Poll::Pending => {} // just keep going - Poll::Ready(()) => self.0 = Done, + Done => { + this.machine.tls.delete_all_thread_tls(this.get_active_thread()); + return Ok(Poll::Ready(())); } } - Done => { - this.machine.tls.delete_all_thread_tls(this.get_active_thread()); - return Ok(Poll::Ready(())); - } - } + }; + self.0 = new_state; Ok(Poll::Pending) } } @@ -282,22 +297,19 @@ impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for crate::MiriInterpCx<'m trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Schedule TLS destructors for Windows. /// On windows, TLS destructors are managed by std. - fn schedule_windows_tls_dtors(&mut self) -> InterpResult<'tcx> { + fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec>> { let this = self.eval_context_mut(); // Windows has a special magic linker section that is run on certain events. - // Instead of searching for that section and supporting arbitrary hooks in there - // (that would be basically https://github.com/rust-lang/miri/issues/450), - // we specifically look up the static in libstd that we know is placed - // in that section. - if !this.have_module(&["std"]) { - // Looks like we are running in a `no_std` crate. - // That also means no TLS dtors callback to call. - return Ok(()); - } - let thread_callback = - this.eval_windows("thread_local_key", "p_thread_callback").to_pointer(this)?; - let thread_callback = this.get_ptr_fn(thread_callback)?.as_instance()?; + // We don't support most of that, but just enough to make thread-local dtors in `std` work. + Ok(this.lookup_link_section(".CRT$XLB")?) + } + + fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx, Provenance>) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let dtor = dtor.to_scalar().to_pointer(this)?; + let thread_callback = this.get_ptr_fn(dtor)?.as_instance()?; // FIXME: Technically, the reason should be `DLL_PROCESS_DETACH` when the main thread exits // but std treats both the same. @@ -305,7 +317,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`. // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown - // but both are ignored by std + // but both are ignored by std. this.call_function( thread_callback, Abi::System { unwind: false }, From e428789998feac0b402ede2fb76549ef85e0beac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 15 Apr 2024 09:35:06 +0200 Subject: [PATCH 022/208] Windows: add basic support for FormatMessageW --- src/helpers.rs | 41 ++++++++++++++++++------------ src/shims/os_str.rs | 1 + src/shims/windows/foreign_items.rs | 38 +++++++++++++++++++++++++++ tests/pass/shims/fs.rs | 2 +- tests/pass/shims/io.rs | 16 ++++++++++-- 5 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 6e320b60ee..84eb5f832d 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -78,6 +78,17 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { ("EAGAIN", WouldBlock), ] }; +// This mapping should match `decode_error_kind` in +// . +const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = { + use std::io::ErrorKind::*; + // FIXME: this is still incomplete. + &[ + ("ERROR_ACCESS_DENIED", PermissionDenied), + ("ERROR_FILE_NOT_FOUND", NotFound), + ("ERROR_INVALID_PARAMETER", InvalidInput), + ] +}; /// Gets an instance for a path. /// @@ -712,20 +723,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind) } else if target.families.iter().any(|f| f == "windows") { - // FIXME: we have to finish implementing the Windows equivalent of this. - use std::io::ErrorKind::*; - Ok(this.eval_windows( - "c", - match err_kind { - NotFound => "ERROR_FILE_NOT_FOUND", - PermissionDenied => "ERROR_ACCESS_DENIED", - _ => - throw_unsup_format!( - "io error {:?} cannot be translated into a raw os error", - err_kind - ), - }, - )) + for &(name, kind) in WINDOWS_IO_ERROR_TABLE { + if err_kind == kind { + return Ok(this.eval_windows("c", name)); + } + } + throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind); } else { throw_unsup_format!( "converting io::Error into errnum is unsupported for OS {}", @@ -749,8 +752,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(Some(kind)); } } - // Our table is as complete as the mapping in std, so we are okay with saying "that's a - // strange one" here. + return Ok(None); + } else if target.families.iter().any(|f| f == "windows") { + let errnum = errnum.to_u32()?; + for &(name, kind) in WINDOWS_IO_ERROR_TABLE { + if errnum == this.eval_windows("c", name).to_u32()? { + return Ok(Some(kind)); + } + } return Ok(None); } else { throw_unsup_format!( diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 62ce2ee58a..74e8d1d9ed 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -98,6 +98,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// /// If `truncate == true`, then in case `size` is not large enough it *will* write the first /// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`). + /// The return value is still `(false, length)` in that case. fn write_os_str_to_wide_str( &mut self, os_str: &OsStr, diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 9c6994cec7..2e514b11cb 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::iter; use std::str; @@ -533,6 +534,43 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.set_last_error(insufficient_buffer)?; } } + "FormatMessageW" => { + let [flags, module, message_id, language_id, buffer, size, arguments] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + + let flags = this.read_scalar(flags)?.to_u32()?; + let _module = this.read_pointer(module)?; // seems to contain a module name + let message_id = this.read_scalar(message_id)?; + let _language_id = this.read_scalar(language_id)?.to_u32()?; + let buffer = this.read_pointer(buffer)?; + let size = this.read_scalar(size)?.to_u32()?; + let _arguments = this.read_pointer(arguments)?; + + // We only support `FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS` + // This also means `arguments` can be ignored. + if flags != 4096u32 | 512u32 { + throw_unsup_format!("FormatMessageW: unsupported flags {flags:#x}"); + } + + let error = this.try_errnum_to_io_error(message_id)?; + let formatted = match error { + Some(err) => format!("{err}"), + None => format!(""), + }; + let (complete, length) = this.write_os_str_to_wide_str( + OsStr::new(&formatted), + buffer, + size.into(), + /*trunacte*/ false, + )?; + if !complete { + // The API docs don't say what happens when the buffer is not big enough... + // Let's just bail. + throw_unsup_format!("FormatMessageW: buffer not big enough"); + } + // The return value is the number of characters stored *excluding* the null terminator. + this.write_int(length.checked_sub(1).unwrap(), dest)?; + } // 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. diff --git a/tests/pass/shims/fs.rs b/tests/pass/shims/fs.rs index d10faebac7..8a500b857b 100644 --- a/tests/pass/shims/fs.rs +++ b/tests/pass/shims/fs.rs @@ -260,7 +260,7 @@ fn test_errors() { // Opening a non-existing file should fail with a "not found" error. assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind()); // Make sure we can also format this. - format!("{0:?}: {0}", File::open(&path).unwrap_err()); + format!("{0}: {0:?}", File::open(&path).unwrap_err()); // Removing a non-existing file should fail with a "not found" error. assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind()); // Reading the metadata of a non-existing file should fail with a "not found" error. diff --git a/tests/pass/shims/io.rs b/tests/pass/shims/io.rs index 295723957a..d20fc75b79 100644 --- a/tests/pass/shims/io.rs +++ b/tests/pass/shims/io.rs @@ -1,7 +1,19 @@ -use std::io::IsTerminal; +use std::io::{self, IsTerminal}; fn main() { // We can't really assume that this is truly a terminal, and anyway on Windows Miri will always // return `false` here, but we can check that the call succeeds. - std::io::stdout().is_terminal(); + io::stdout().is_terminal(); + + // Ensure we can format `io::Error` created from OS errors + // (calls OS-specific error formatting functions). + let raw_os_error = if cfg!(unix) { + 22 // EINVAL (on most Unixes, anyway) + } else if cfg!(windows) { + 87 // ERROR_INVALID_PARAMETER + } else { + panic!("unsupported OS") + }; + let err = io::Error::from_raw_os_error(raw_os_error); + format!("{err}: {err:?}"); } From 204326dd9fa1410b055c75f27280e1f56413dd53 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 15 Apr 2024 09:53:32 +0200 Subject: [PATCH 023/208] fix error display for './miri run --dep' --- tests/ui.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/ui.rs b/tests/ui.rs index 7e8d140118..68446f40e0 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -296,12 +296,13 @@ fn main() -> Result<()> { fn run_dep_mode(target: String, mut args: impl Iterator) -> Result<()> { let path = args.next().expect("./miri run-dep must be followed by a file name"); - let config = miri_config( + let mut config = miri_config( &target, "", Mode::Yolo { rustfix: RustfixMode::Disabled }, /* with dependencies */ true, ); + config.program.args.clear(); // remove the `--error-format` that ui_test adds by default let dep_args = config.build_dependencies()?; let mut cmd = config.program.build(&config.out_dir); From f592764197a5e9858b8c10a0a82bad07d3c0e8ef Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 15 Apr 2024 14:32:22 +0200 Subject: [PATCH 024/208] add some basic support for GetFullPathNameW --- src/helpers.rs | 15 +++++ src/lib.rs | 1 + src/shims/env.rs | 25 +++----- src/shims/os_str.rs | 24 +++++++- src/shims/windows/foreign_items.rs | 93 +++++++++++++++++++++++++++++- tests/pass/shims/path.rs | 38 ++++++++++++ 6 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 tests/pass/shims/path.rs diff --git a/src/helpers.rs b/src/helpers.rs index 84eb5f832d..998de80a7e 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1285,3 +1285,18 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult< _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"), }) } + +/// Check whether an operation that writes to a target buffer was successful. +/// Accordingly select return value. +/// Local helper function to be used in Windows shims. +pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { + if success { + // If the function succeeds, the return value is the number of characters stored in the target buffer, + // not including the terminating null character. + u32::try_from(len.checked_sub(1).unwrap()).unwrap() + } else { + // If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters, + // required to hold the string and its terminating null character. + u32::try_from(len).unwrap() + } +} diff --git a/src/lib.rs b/src/lib.rs index 7821aa9efd..484908086a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ #![feature(let_chains)] #![feature(lint_reasons)] #![feature(trait_upcasting)] +#![feature(absolute_path)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/shims/env.rs b/src/shims/env.rs index 9e8239cdba..1779189c9c 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -9,21 +9,7 @@ use rustc_middle::ty::Ty; use rustc_target::abi::Size; use crate::*; - -/// Check whether an operation that writes to a target buffer was successful. -/// Accordingly select return value. -/// Local helper function to be used in Windows shims. -fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 { - if success { - // If the function succeeds, the return value is the number of characters stored in the target buffer, - // not including the terminating null character. - u32::try_from(len.checked_sub(1).unwrap()).unwrap() - } else { - // If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters, - // required to hold the string and its terminating null character. - u32::try_from(len).unwrap() - } -} +use helpers::windows_check_buffer_size; #[derive(Default)] pub struct EnvVars<'tcx> { @@ -164,7 +150,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name_ptr = this.read_pointer(name_op)?; let name = this.read_os_str_from_wide_str(name_ptr)?; Ok(match this.machine.env_vars.map.get(&name) { - Some(var_ptr) => { + Some(&var_ptr) => { + this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error // The offset is used to strip the "{name}=" part of the string. #[rustfmt::skip] let name_offset_bytes = u64::try_from(name.len()).unwrap() @@ -375,10 +362,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // If we cannot get the current directory, we return 0 match env::current_dir() { - Ok(cwd) => + Ok(cwd) => { + this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error return Ok(Scalar::from_u32(windows_check_buffer_size( this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?, - ))), + ))); + } Err(e) => this.set_last_error_from_io_error(e.kind())?, } Ok(Scalar::from_u32(0)) diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 74e8d1d9ed..0157c4845c 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -316,9 +316,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We also have to ensure that absolute paths remain absolute. match direction { PathConversion::HostToTarget => { - // If this start withs a `\`, we add `\\?` so it starts with `\\?\` which is - // some magic path on Windows that *is* considered absolute. - if converted.get(0).copied() == Some(b'\\') { + // If the path is `/C:/`, the leading backslash was probably added by the below + // driver letter handling and we should get rid of it again. + if converted.get(0).copied() == Some(b'\\') + && converted.get(2).copied() == Some(b':') + && converted.get(3).copied() == Some(b'\\') + { + converted.remove(0); + } + // If this start withs a `\` but not a `\\`, then for Windows this is a relative + // path. But the host path is absolute as it started with `/`. We add `\\?` so + // it starts with `\\?\` which is some magic path on Windows that *is* + // considered absolute. + else if converted.get(0).copied() == Some(b'\\') + && converted.get(1).copied() != Some(b'\\') + { converted.splice(0..0, b"\\\\?".iter().copied()); } } @@ -333,6 +345,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Remove first 3 characters converted.splice(0..3, std::iter::empty()); } + // If it starts with a drive letter, convert it to an absolute Unix path. + else if converted.get(1).copied() == Some(b':') + && converted.get(2).copied() == Some(b'/') + { + converted.insert(0, b'/'); + } } } Cow::Owned(OsString::from_vec(converted)) diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 2e514b11cb..de80df3c80 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -1,5 +1,7 @@ use std::ffi::OsStr; +use std::io; use std::iter; +use std::path::{self, Path, PathBuf}; use std::str; use rustc_span::Symbol; @@ -21,6 +23,61 @@ fn is_dyn_sym(name: &str) -> bool { ) } +#[cfg(windows)] +fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { + // We are on Windows so we can simply lte the host do this. + return Ok(path::absolute(path)); +} + +#[cfg(unix)] +#[allow(clippy::get_first, clippy::arithmetic_side_effects)] +fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { + // We are on Unix, so we need to implement parts of the logic ourselves. + let bytes = path.as_os_str().as_encoded_bytes(); + // If it starts with `//` (these were backslashes but are already converted) + // then this is a magic special path, we just leave it unchanged. + if bytes.get(0).copied() == Some(b'/') && bytes.get(1).copied() == Some(b'/') { + return Ok(Ok(path.into())); + }; + // Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`. + let magic_filenames = &[ + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", + "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + ]; + if magic_filenames.iter().any(|m| m.as_bytes() == bytes) { + let mut result: Vec = br"//./".into(); + result.extend(bytes); + return Ok(Ok(bytes_to_os_str(&result)?.into())); + } + // Otherwise we try to do something kind of close to what Windows does, but this is probably not + // right in all cases. We iterate over the components between `/`, and remove trailing `.`, + // except that trailing `..` remain unchanged. + let mut result = vec![]; + let mut bytes = bytes; // the remaining bytes to process + loop { + let len = bytes.iter().position(|&b| b == b'/').unwrap_or(bytes.len()); + let mut component = &bytes[..len]; + if len >= 2 && component[len - 1] == b'.' && component[len - 2] != b'.' { + // Strip trailing `.` + component = &component[..len - 1]; + } + // Add this component to output. + result.extend(component); + // Prepare next iteration. + if len < bytes.len() { + // There's a component after this; add `/` and process remaining bytes. + result.push(b'/'); + bytes = &bytes[len + 1..]; + continue; + } else { + // This was the last component and it did not have a trailing `/`. + break; + } + } + // Let the host `absolute` function do working-dir handling + Ok(path::absolute(bytes_to_os_str(&result)?)) +} + impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn emulate_foreign_item_inner( @@ -112,7 +169,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let written = if handle == -11 || handle == -12 { // stdout/stderr - use std::io::{self, Write}; + use io::Write; let buf_cont = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?; @@ -146,6 +203,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest, )?; } + "GetFullPathNameW" => { + let [filename, size, buffer, filepart] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + this.check_no_isolation("`GetFullPathNameW`")?; + + let filename = this.read_pointer(filename)?; + let size = this.read_scalar(size)?.to_u32()?; + let buffer = this.read_pointer(buffer)?; + let filepart = this.read_pointer(filepart)?; + + if !this.ptr_is_null(filepart)? { + throw_unsup_format!("GetFullPathNameW: non-null `lpFilePart` is not supported"); + } + + let filename = this.read_path_from_wide_str(filename)?; + let result = match win_absolute(&filename)? { + Err(err) => { + this.set_last_error_from_io_error(err.kind())?; + Scalar::from_u32(0) // return zero upon failure + } + Ok(abs_filename) => { + this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error + Scalar::from_u32(helpers::windows_check_buffer_size( + this.write_path_to_wide_str( + &abs_filename, + buffer, + size.into(), + /*truncate*/ false, + )?, + )) + } + }; + this.write_scalar(result, dest)?; + } // Allocation "HeapAlloc" => { diff --git a/tests/pass/shims/path.rs b/tests/pass/shims/path.rs new file mode 100644 index 0000000000..9fc6e7faef --- /dev/null +++ b/tests/pass/shims/path.rs @@ -0,0 +1,38 @@ +//@compile-flags: -Zmiri-disable-isolation +#![feature(absolute_path)] +use std::path::{absolute, Path}; + +#[track_caller] +fn test_absolute(in_: &str, out: &str) { + assert_eq!(absolute(in_).unwrap().as_os_str(), Path::new(out).as_os_str()); +} + +fn main() { + if cfg!(unix) { + test_absolute("/a/b/c", "/a/b/c"); + test_absolute("/a/b/c", "/a/b/c"); + test_absolute("/a//b/c", "/a/b/c"); + test_absolute("//a/b/c", "//a/b/c"); + test_absolute("///a/b/c", "/a/b/c"); + test_absolute("/a/b/c/", "/a/b/c/"); + test_absolute("/a/./b/../c/.././..", "/a/b/../c/../.."); + } else if cfg!(windows) { + // Test that all these are unchanged + test_absolute(r"C:\path\to\file", r"C:\path\to\file"); + test_absolute(r"C:\path\to\file\", r"C:\path\to\file\"); + test_absolute(r"\\server\share\to\file", r"\\server\share\to\file"); + test_absolute(r"\\server.\share.\to\file", r"\\server.\share.\to\file"); + test_absolute(r"\\.\PIPE\name", r"\\.\PIPE\name"); + test_absolute(r"\\.\C:\path\to\COM1", r"\\.\C:\path\to\COM1"); + test_absolute(r"\\?\C:\path\to\file", r"\\?\C:\path\to\file"); + test_absolute(r"\\?\UNC\server\share\to\file", r"\\?\UNC\server\share\to\file"); + test_absolute(r"\\?\PIPE\name", r"\\?\PIPE\name"); + // Verbatim paths are always unchanged, no matter what. + test_absolute(r"\\?\path.\to/file..", r"\\?\path.\to/file.."); + + test_absolute(r"C:\path..\to.\file.", r"C:\path..\to\file"); + test_absolute(r"COM1", r"\\.\COM1"); + } else { + panic!("unsupported OS"); + } +} From 2d90a21dbf4b4ab69110d7f9ae4a8b8eb6d9648e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 14 Apr 2024 19:30:20 +0200 Subject: [PATCH 025/208] Make `split_simd_to_128bit_chunks` take only one operand It will allow more flexible uses in the future. This makes `split_simd_to_128bit_chunks` simpler, moving some of the complexity to its callers. --- src/shims/x86/mod.rs | 77 +++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 2a663d300a..615821b2e3 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -664,54 +664,35 @@ fn convert_float_to_int<'tcx>( Ok(()) } -/// Splits `left`, `right` and `dest` (which must be SIMD vectors) -/// into 128-bit chuncks. -/// -/// `left`, `right` and `dest` cannot have different types. +/// Splits `op` (which must be a SIMD vector) into 128-bit chuncks. /// /// Returns a tuple where: /// * The first element is the number of 128-bit chunks (let's call it `N`). /// * The second element is the number of elements per chunk (let's call it `M`). -/// * The third element is the `left` vector split into chunks, i.e, it's -/// type is `[[T; M]; N]`. -/// * The fourth element is the `right` vector split into chunks. -/// * The fifth element is the `dest` vector split into chunks. -fn split_simd_to_128bit_chunks<'tcx>( +/// * The third element is the `op` vector split into chunks, i.e, it's +/// type is `[[T; M]; N]` where `T` is the element type of `op`. +fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>( this: &mut crate::MiriInterpCx<'_, 'tcx>, - left: &OpTy<'tcx, Provenance>, - right: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, -) -> InterpResult< - 'tcx, - (u64, u64, MPlaceTy<'tcx, Provenance>, MPlaceTy<'tcx, Provenance>, MPlaceTy<'tcx, Provenance>), -> { - assert_eq!(dest.layout, left.layout); - assert_eq!(dest.layout, right.layout); + op: &P, +) -> InterpResult<'tcx, (u64, u64, P)> { + let simd_layout = op.layout(); + let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(this.tcx.tcx); - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - assert_eq!(dest.layout.size.bits() % 128, 0); - let num_chunks = dest.layout.size.bits() / 128; - assert_eq!(dest_len.checked_rem(num_chunks), Some(0)); - let items_per_chunk = dest_len.checked_div(num_chunks).unwrap(); + assert_eq!(simd_layout.size.bits() % 128, 0); + let num_chunks = simd_layout.size.bits() / 128; + let items_per_chunk = simd_len.checked_div(num_chunks).unwrap(); // Transmute to `[[T; items_per_chunk]; num_chunks]` - let element_layout = left.layout.field(this, 0); - let chunked_layout = this.layout_of(Ty::new_array( - this.tcx.tcx, - Ty::new_array(this.tcx.tcx, element_layout.ty, items_per_chunk), - num_chunks, - ))?; - let left = left.transmute(chunked_layout, this)?; - let right = right.transmute(chunked_layout, this)?; - let dest = dest.transmute(chunked_layout, this)?; - - Ok((num_chunks, items_per_chunk, left, right, dest)) + let chunked_layout = this + .layout_of(Ty::new_array( + this.tcx.tcx, + Ty::new_array(this.tcx.tcx, element_ty, items_per_chunk), + num_chunks, + )) + .unwrap(); + let chunked_op = op.transmute(chunked_layout, this)?; + + Ok((num_chunks, items_per_chunk, chunked_op)) } /// Horizontaly performs `which` operation on adjacent values of @@ -731,8 +712,12 @@ fn horizontal_bin_op<'tcx>( right: &OpTy<'tcx, Provenance>, dest: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ()> { - let (num_chunks, items_per_chunk, left, right, dest) = - split_simd_to_128bit_chunks(this, left, right, dest)?; + assert_eq!(left.layout, dest.layout); + assert_eq!(right.layout, dest.layout); + + let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?; + let (_, _, right) = split_simd_to_128bit_chunks(this, right)?; + let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?; let middle = items_per_chunk / 2; for i in 0..num_chunks { @@ -779,8 +764,12 @@ fn conditional_dot_product<'tcx>( imm: &OpTy<'tcx, Provenance>, dest: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ()> { - let (num_chunks, items_per_chunk, left, right, dest) = - split_simd_to_128bit_chunks(this, left, right, dest)?; + assert_eq!(left.layout, dest.layout); + assert_eq!(right.layout, dest.layout); + + let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?; + let (_, _, right) = split_simd_to_128bit_chunks(this, right)?; + let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?; let element_layout = left.layout.field(this, 0).field(this, 0); assert!(items_per_chunk <= 4); From 69a6c8c2d4bc428633239151cdd03702a256851e Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:51:56 -0400 Subject: [PATCH 026/208] Bump rustc-build-sysroot to 0.4.6 --- cargo-miri/Cargo.lock | 4 ++-- cargo-miri/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cargo-miri/Cargo.lock b/cargo-miri/Cargo.lock index 6841a345ce..a1dbc85605 100644 --- a/cargo-miri/Cargo.lock +++ b/cargo-miri/Cargo.lock @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26170e1d79ea32f7ccec3188dd13cfc1f18c82764a9cbc1071667c0f865a4ea" +checksum = "a9bf37423495cd3a6a9ef8c75fc4566de0d26de0ab75f36f67a426e58df5768c" dependencies = [ "anyhow", "rustc_version", diff --git a/cargo-miri/Cargo.toml b/cargo-miri/Cargo.toml index 55f6b5ac7e..b16068b6d1 100644 --- a/cargo-miri/Cargo.toml +++ b/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "5" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.18.0" -rustc-build-sysroot = "0.4.1" +rustc-build-sysroot = "0.4.6" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. From d400c881197c2c20e72c0ea23a2908793e8b5f41 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 16 Apr 2024 05:15:16 +0000 Subject: [PATCH 027/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 6ad8fba723..dfa7f8ca50 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -23d47dba319331d4418827cfbb8c1af283497d3c +63f70b3d104e20289a1a0df82747066c3d85b9a1 From db7cbf2c846d9eb0edd7ed768aba07ef14b75e50 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 08:03:48 +0200 Subject: [PATCH 028/208] bless test-cargo-miri --- test-cargo-miri/test.bin-target.stdout.ref | 2 +- test-cargo-miri/test.cross-target.stdout.ref | 4 ++-- test-cargo-miri/test.default.stdout.ref | 4 ++-- test-cargo-miri/test.filter.cross-target.stdout.ref | 4 ++-- test-cargo-miri/test.filter.stdout.ref | 4 ++-- test-cargo-miri/test.subcrate.stdout.ref | 2 +- test-cargo-miri/test.test-target.stdout.ref | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test-cargo-miri/test.bin-target.stdout.ref b/test-cargo-miri/test.bin-target.stdout.ref index 5264530160..6f48025996 100644 --- a/test-cargo-miri/test.bin-target.stdout.ref +++ b/test-cargo-miri/test.bin-target.stdout.ref @@ -3,5 +3,5 @@ running 2 tests test test::dev_dependency ... ok test test::exported_symbol ... ok -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/test-cargo-miri/test.cross-target.stdout.ref b/test-cargo-miri/test.cross-target.stdout.ref index 8c543e479f..2ef124e4de 100644 --- a/test-cargo-miri/test.cross-target.stdout.ref +++ b/test-cargo-miri/test.cross-target.stdout.ref @@ -1,11 +1,11 @@ running 2 tests .. -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME imported main running 6 tests ...i.. -test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/test-cargo-miri/test.default.stdout.ref b/test-cargo-miri/test.default.stdout.ref index 922d2120be..2d74d82f76 100644 --- a/test-cargo-miri/test.default.stdout.ref +++ b/test-cargo-miri/test.default.stdout.ref @@ -1,13 +1,13 @@ running 2 tests .. -test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME imported main running 6 tests ...i.. -test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME running 5 tests diff --git a/test-cargo-miri/test.filter.cross-target.stdout.ref b/test-cargo-miri/test.filter.cross-target.stdout.ref index bb0282d6c9..59b4deb1ff 100644 --- a/test-cargo-miri/test.filter.cross-target.stdout.ref +++ b/test-cargo-miri/test.filter.cross-target.stdout.ref @@ -1,12 +1,12 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME imported main running 1 test test simple ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME diff --git a/test-cargo-miri/test.filter.stdout.ref b/test-cargo-miri/test.filter.stdout.ref index 5c819dd532..b68bc98327 100644 --- a/test-cargo-miri/test.filter.stdout.ref +++ b/test-cargo-miri/test.filter.stdout.ref @@ -1,14 +1,14 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out; finished in $TIME imported main running 1 test test simple ... ok -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME running 0 tests diff --git a/test-cargo-miri/test.subcrate.stdout.ref b/test-cargo-miri/test.subcrate.stdout.ref index 67e5c7f8e9..e50838ebc8 100644 --- a/test-cargo-miri/test.subcrate.stdout.ref +++ b/test-cargo-miri/test.subcrate.stdout.ref @@ -1,6 +1,6 @@ running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME subcrate testing diff --git a/test-cargo-miri/test.test-target.stdout.ref b/test-cargo-miri/test.test-target.stdout.ref index dd59b32b78..38b3f5c098 100644 --- a/test-cargo-miri/test.test-target.stdout.ref +++ b/test-cargo-miri/test.test-target.stdout.ref @@ -7,5 +7,5 @@ test does_not_work_on_miri ... ignored test fail_index_check - should panic ... ok test simple ... ok -test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out +test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME From d66d33b90a27c0fc53fa0c0a9a4f0f52eed5bf57 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 08:52:06 +0200 Subject: [PATCH 029/208] avoid passing --sysroot twice in bootstrap --- cargo-miri/src/phases.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index ca8b35a17b..b774ca8fa7 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -412,8 +412,11 @@ pub fn phase_rustc(mut args: impl Iterator, phase: RustcPhase) { // Arguments are treated very differently depending on whether this crate is // for interpretation by Miri, or for use by a build script / proc macro. if target_crate { - // Set the sysroot. - cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); + if phase != RustcPhase::Setup { + // Set the sysroot -- except during setup, where we don't have an existing sysroot yet + // and where the bootstrap wrapper adds its own `--sysroot` flag so we can't set ours. + cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); + } // Forward arguments, but patched. let emit_flag = "--emit"; @@ -578,9 +581,9 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner } if phase != RunnerPhase::Rustdoc { - // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot when invoking - // rustdoc itself, which will forward that flag when invoking rustc (i.e., us), so the flag - // is present in `info.args`. + // Set the sysroot. Not necessary in rustdoc, where we already set the sysroot in + // `phase_rustdoc`. rustdoc will forward that flag when invoking rustc (i.e., us), so the + // flag is present in `info.args`. cmd.arg("--sysroot").arg(env::var_os("MIRI_SYSROOT").unwrap()); } // Forward rustc arguments. From baf32fd5c019785c26836145a4b0eca54326217c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 11:29:04 +0200 Subject: [PATCH 030/208] threads: keep track of why we are blocked, and sanity-check that when waking up --- src/concurrency/init_once.rs | 4 +- src/concurrency/sync.rs | 32 +++++----------- src/concurrency/thread.rs | 74 ++++++++++++++++++++++-------------- src/lib.rs | 4 +- src/shims/time.rs | 10 ++--- src/shims/unix/linux/sync.rs | 23 +++++++---- src/shims/unix/sync.rs | 40 +++++++++---------- src/shims/windows/sync.rs | 16 ++++---- 8 files changed, 108 insertions(+), 95 deletions(-) diff --git a/src/concurrency/init_once.rs b/src/concurrency/init_once.rs index 35dcfecbbe..9dbea08f3e 100644 --- a/src/concurrency/init_once.rs +++ b/src/concurrency/init_once.rs @@ -77,7 +77,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let current_thread = this.get_active_thread(); - this.unblock_thread(waiter.thread); + this.unblock_thread(waiter.thread, BlockReason::InitOnce(id)); // Call callback, with the woken-up thread as `current`. this.set_active_thread(waiter.thread); @@ -142,7 +142,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let init_once = &mut this.machine.threads.sync.init_onces[id]; assert_ne!(init_once.status, InitOnceStatus::Complete, "queueing on complete init once"); init_once.waiters.push_back(InitOnceWaiter { thread, callback }); - this.block_thread(thread); + this.block_thread(thread, BlockReason::InitOnce(id)); } /// Begin initializing this InitOnce. Must only be called after checking that it is currently diff --git a/src/concurrency/sync.rs b/src/concurrency/sync.rs index 956a02ded0..0a42871569 100644 --- a/src/concurrency/sync.rs +++ b/src/concurrency/sync.rs @@ -115,25 +115,13 @@ struct RwLock { declare_id!(CondvarId); -#[derive(Debug, Copy, Clone)] -pub enum RwLockMode { - Read, - Write, -} - -#[derive(Debug)] -pub enum CondvarLock { - Mutex(MutexId), - RwLock { id: RwLockId, mode: RwLockMode }, -} - /// A thread waiting on a conditional variable. #[derive(Debug)] struct CondvarWaiter { /// The thread that is waiting on this variable. thread: ThreadId, - /// The mutex or rwlock on which the thread is waiting. - lock: CondvarLock, + /// The mutex on which the thread is waiting. + lock: MutexId, } /// The conditional variable state. @@ -232,7 +220,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: fn rwlock_dequeue_and_lock_reader(&mut self, id: RwLockId) -> bool { let this = self.eval_context_mut(); if let Some(reader) = this.machine.threads.sync.rwlocks[id].reader_queue.pop_front() { - this.unblock_thread(reader); + this.unblock_thread(reader, BlockReason::RwLock(id)); this.rwlock_reader_lock(id, reader); true } else { @@ -246,7 +234,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: fn rwlock_dequeue_and_lock_writer(&mut self, id: RwLockId) -> bool { let this = self.eval_context_mut(); if let Some(writer) = this.machine.threads.sync.rwlocks[id].writer_queue.pop_front() { - this.unblock_thread(writer); + this.unblock_thread(writer, BlockReason::RwLock(id)); this.rwlock_writer_lock(id, writer); true } else { @@ -260,7 +248,7 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>: fn mutex_dequeue_and_lock(&mut self, id: MutexId) -> bool { let this = self.eval_context_mut(); if let Some(thread) = this.machine.threads.sync.mutexes[id].queue.pop_front() { - this.unblock_thread(thread); + this.unblock_thread(thread, BlockReason::Mutex(id)); this.mutex_lock(id, thread); true } else { @@ -406,7 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); assert!(this.mutex_is_locked(id), "queing on unlocked mutex"); this.machine.threads.sync.mutexes[id].queue.push_back(thread); - this.block_thread(thread); + this.block_thread(thread, BlockReason::Mutex(id)); } /// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None, @@ -511,7 +499,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock"); this.machine.threads.sync.rwlocks[id].reader_queue.push_back(reader); - this.block_thread(reader); + this.block_thread(reader, BlockReason::RwLock(id)); } /// Lock by setting the writer that owns the lock. @@ -573,7 +561,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock"); this.machine.threads.sync.rwlocks[id].writer_queue.push_back(writer); - this.block_thread(writer); + this.block_thread(writer, BlockReason::RwLock(id)); } /// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None, @@ -605,7 +593,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Mark that the thread is waiting on the conditional variable. - fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: CondvarLock) { + fn condvar_wait(&mut self, id: CondvarId, thread: ThreadId, lock: MutexId) { let this = self.eval_context_mut(); let waiters = &mut this.machine.threads.sync.condvars[id].waiters; assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting"); @@ -614,7 +602,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Wake up some thread (if there is any) sleeping on the conditional /// variable. - fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, CondvarLock)> { + fn condvar_signal(&mut self, id: CondvarId) -> Option<(ThreadId, MutexId)> { let this = self.eval_context_mut(); let current_thread = this.get_active_thread(); let current_span = this.machine.current_span(); diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index d1136272f0..06e2318833 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -88,18 +88,33 @@ impl From for u64 { } } +/// Keeps track of what the thread is blocked on. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum BlockReason { + /// The thread tried to join the specified thread and is blocked until that + /// thread terminates. + Join(ThreadId), + /// Waiting for time to pass. + Sleep, + /// Blocked on a mutex. + Mutex(MutexId), + /// Blocked on a condition variable. + Condvar(CondvarId), + /// Blocked on a reader-writer lock. + RwLock(RwLockId), + /// Blocled on a Futex variable. + Futex { addr: u64 }, + /// Blocked on an InitOnce. + InitOnce(InitOnceId), +} + /// The state of a thread. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ThreadState { /// The thread is enabled and can be executed. Enabled, - /// The thread tried to join the specified thread and is blocked until that - /// thread terminates. - BlockedOnJoin(ThreadId), - /// The thread is blocked on some synchronization primitive. It is the - /// responsibility of the synchronization primitives to track threads that - /// are blocked by them. - BlockedOnSync, + /// The thread is blocked on something. + Blocked(BlockReason), /// The thread has terminated its execution. We do not delete terminated /// threads (FIXME: why?). Terminated, @@ -296,17 +311,17 @@ impl VisitProvenance for Frame<'_, '_, Provenance, FrameExtra<'_>> { /// A specific moment in time. #[derive(Debug)] -pub enum Time { +pub enum CallbackTime { Monotonic(Instant), RealTime(SystemTime), } -impl Time { +impl CallbackTime { /// How long do we have to wait from now until the specified time? fn get_wait_time(&self, clock: &Clock) -> Duration { match self { - Time::Monotonic(instant) => instant.duration_since(clock.now()), - Time::RealTime(time) => + CallbackTime::Monotonic(instant) => instant.duration_since(clock.now()), + CallbackTime::RealTime(time) => time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)), } } @@ -318,7 +333,7 @@ impl Time { /// conditional variable, the signal handler deletes the callback. struct TimeoutCallbackInfo<'mir, 'tcx> { /// The callback should be called no earlier than this time. - call_time: Time, + call_time: CallbackTime, /// The called function. callback: TimeoutCallback<'mir, 'tcx>, } @@ -539,7 +554,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { self.threads[joined_thread_id].join_status = ThreadJoinStatus::Joined; if self.threads[joined_thread_id].state != ThreadState::Terminated { // The joined thread is still running, we need to wait for it. - self.active_thread_mut().state = ThreadState::BlockedOnJoin(joined_thread_id); + self.active_thread_mut().state = + ThreadState::Blocked(BlockReason::Join(joined_thread_id)); trace!( "{:?} blocked on {:?} when trying to join", self.active_thread, @@ -569,10 +585,11 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { throw_ub_format!("trying to join itself"); } + // Sanity check `join_status`. assert!( - self.threads - .iter() - .all(|thread| thread.state != ThreadState::BlockedOnJoin(joined_thread_id)), + self.threads.iter().all(|thread| { + thread.state != ThreadState::Blocked(BlockReason::Join(joined_thread_id)) + }), "this thread already has threads waiting for its termination" ); @@ -594,16 +611,17 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { } /// Put the thread into the blocked state. - fn block_thread(&mut self, thread: ThreadId) { + fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) { let state = &mut self.threads[thread].state; assert_eq!(*state, ThreadState::Enabled); - *state = ThreadState::BlockedOnSync; + *state = ThreadState::Blocked(reason); } /// Put the blocked thread into the enabled state. - fn unblock_thread(&mut self, thread: ThreadId) { + /// Sanity-checks that the thread previously was blocked for the right reason. + fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) { let state = &mut self.threads[thread].state; - assert_eq!(*state, ThreadState::BlockedOnSync); + assert_eq!(*state, ThreadState::Blocked(reason)); *state = ThreadState::Enabled; } @@ -622,7 +640,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { fn register_timeout_callback( &mut self, thread: ThreadId, - call_time: Time, + call_time: CallbackTime, callback: TimeoutCallback<'mir, 'tcx>, ) { self.timeout_callbacks @@ -683,7 +701,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { // Check if we need to unblock any threads. let mut joined_threads = vec![]; // store which threads joined, we'll need it for (i, thread) in self.threads.iter_enumerated_mut() { - if thread.state == ThreadState::BlockedOnJoin(self.active_thread) { + if thread.state == ThreadState::Blocked(BlockReason::Join(self.active_thread)) { // The thread has terminated, mark happens-before edge to joining thread if data_race.is_some() { joined_threads.push(i); @@ -999,13 +1017,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } #[inline] - fn block_thread(&mut self, thread: ThreadId) { - self.eval_context_mut().machine.threads.block_thread(thread); + fn block_thread(&mut self, thread: ThreadId, reason: BlockReason) { + self.eval_context_mut().machine.threads.block_thread(thread, reason); } #[inline] - fn unblock_thread(&mut self, thread: ThreadId) { - self.eval_context_mut().machine.threads.unblock_thread(thread); + fn unblock_thread(&mut self, thread: ThreadId, reason: BlockReason) { + self.eval_context_mut().machine.threads.unblock_thread(thread, reason); } #[inline] @@ -1027,11 +1045,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn register_timeout_callback( &mut self, thread: ThreadId, - call_time: Time, + call_time: CallbackTime, callback: TimeoutCallback<'mir, 'tcx>, ) { let this = self.eval_context_mut(); - if !this.machine.communicate() && matches!(call_time, Time::RealTime(..)) { + if !this.machine.communicate() && matches!(call_time, CallbackTime::RealTime(..)) { panic!("cannot have `RealTime` callback with isolation enabled!") } this.machine.threads.register_timeout_callback(thread, call_time, callback); diff --git a/src/lib.rs b/src/lib.rs index 484908086a..fbe4c9c676 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,7 +116,9 @@ pub use crate::concurrency::{ data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _}, init_once::{EvalContextExt as _, InitOnceId}, sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SyncId}, - thread::{EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, Time}, + thread::{ + BlockReason, CallbackTime, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, + }, }; pub use crate::diagnostics::{ report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, diff --git a/src/shims/time.rs b/src/shims/time.rs index 4535bcf6df..1126c90022 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -236,11 +236,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap()); let active_thread = this.get_active_thread(); - this.block_thread(active_thread); + this.block_thread(active_thread, BlockReason::Sleep); this.register_timeout_callback( active_thread, - Time::Monotonic(timeout_time), + CallbackTime::Monotonic(timeout_time), Box::new(UnblockCallback { thread_to_unblock: active_thread }), ); @@ -259,11 +259,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let timeout_time = this.machine.clock.now().checked_add(duration).unwrap(); let active_thread = this.get_active_thread(); - this.block_thread(active_thread); + this.block_thread(active_thread, BlockReason::Sleep); this.register_timeout_callback( active_thread, - Time::Monotonic(timeout_time), + CallbackTime::Monotonic(timeout_time), Box::new(UnblockCallback { thread_to_unblock: active_thread }), ); @@ -281,7 +281,7 @@ impl VisitProvenance for UnblockCallback { impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for UnblockCallback { fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - ecx.unblock_thread(self.thread_to_unblock); + ecx.unblock_thread(self.thread_to_unblock, BlockReason::Sleep); Ok(()) } } diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index ed27066aa6..d4a6cd96f4 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -107,16 +107,22 @@ pub fn futex<'tcx>( Some(if wait_bitset { // FUTEX_WAIT_BITSET uses an absolute timestamp. if realtime { - Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) + CallbackTime::RealTime( + SystemTime::UNIX_EPOCH.checked_add(duration).unwrap(), + ) } else { - Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) + CallbackTime::Monotonic( + this.machine.clock.anchor().checked_add(duration).unwrap(), + ) } } else { // FUTEX_WAIT uses a relative timestamp. if realtime { - Time::RealTime(SystemTime::now().checked_add(duration).unwrap()) + CallbackTime::RealTime(SystemTime::now().checked_add(duration).unwrap()) } else { - Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap()) + CallbackTime::Monotonic( + this.machine.clock.now().checked_add(duration).unwrap(), + ) } }) }; @@ -169,7 +175,7 @@ pub fn futex<'tcx>( let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?; if val == futex_val { // The value still matches, so we block the thread make it wait for FUTEX_WAKE. - this.block_thread(thread); + this.block_thread(thread, BlockReason::Futex { addr: addr_usize }); this.futex_wait(addr_usize, thread, bitset); // Succesfully waking up from FUTEX_WAIT always returns zero. this.write_scalar(Scalar::from_target_isize(0, this), dest)?; @@ -191,7 +197,10 @@ pub fn futex<'tcx>( impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - this.unblock_thread(self.thread); + this.unblock_thread( + self.thread, + BlockReason::Futex { addr: self.addr_usize }, + ); this.futex_remove_waiter(self.addr_usize, self.thread); let etimedout = this.eval_libc("ETIMEDOUT"); this.set_last_error(etimedout)?; @@ -249,7 +258,7 @@ pub fn futex<'tcx>( #[allow(clippy::arithmetic_side_effects)] for _ in 0..val { if let Some(thread) = this.futex_wake(addr_usize, bitset) { - this.unblock_thread(thread); + this.unblock_thread(thread, BlockReason::Futex { addr: addr_usize }); this.unregister_timeout_callback_if_exists(thread); n += 1; } else { diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index dd301f9ee6..e50a8934e0 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -1,6 +1,5 @@ use std::time::SystemTime; -use crate::concurrency::sync::CondvarLock; use crate::concurrency::thread::MachineCallback; use crate::*; @@ -225,9 +224,10 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( fn reacquire_cond_mutex<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, thread: ThreadId, + condvar: CondvarId, mutex: MutexId, ) -> InterpResult<'tcx> { - ecx.unblock_thread(thread); + ecx.unblock_thread(thread, BlockReason::Condvar(condvar)); if ecx.mutex_is_locked(mutex) { ecx.mutex_enqueue_and_block(mutex, thread); } else { @@ -242,9 +242,10 @@ fn reacquire_cond_mutex<'mir, 'tcx: 'mir>( fn post_cond_signal<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, thread: ThreadId, + condvar: CondvarId, mutex: MutexId, ) -> InterpResult<'tcx> { - reacquire_cond_mutex(ecx, thread, mutex)?; + reacquire_cond_mutex(ecx, thread, condvar, mutex)?; // Waiting for the mutex is not included in the waiting time because we need // to acquire the mutex always even if we get a timeout. ecx.unregister_timeout_callback_if_exists(thread); @@ -256,6 +257,7 @@ fn post_cond_signal<'mir, 'tcx: 'mir>( fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, active_thread: ThreadId, + condvar: CondvarId, mutex: MutexId, ) -> InterpResult<'tcx> { if let Some(old_locked_count) = ecx.mutex_unlock(mutex, active_thread) { @@ -265,7 +267,7 @@ fn release_cond_mutex_and_block<'mir, 'tcx: 'mir>( } else { throw_ub_format!("awaiting on unlocked or owned by a different thread mutex"); } - ecx.block_thread(active_thread); + ecx.block_thread(active_thread, BlockReason::Condvar(condvar)); Ok(()) } @@ -792,12 +794,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; - if let Some((thread, lock)) = this.condvar_signal(id) { - if let CondvarLock::Mutex(mutex) = lock { - post_cond_signal(this, thread, mutex)?; - } else { - panic!("condvar should not have an rwlock on unix"); - } + if let Some((thread, mutex)) = this.condvar_signal(id) { + post_cond_signal(this, thread, id, mutex)?; } Ok(0) @@ -810,12 +808,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let id = cond_get_id(this, cond_op)?; - while let Some((thread, lock)) = this.condvar_signal(id) { - if let CondvarLock::Mutex(mutex) = lock { - post_cond_signal(this, thread, mutex)?; - } else { - panic!("condvar should not have an rwlock on unix"); - } + while let Some((thread, mutex)) = this.condvar_signal(id) { + post_cond_signal(this, thread, id, mutex)?; } Ok(0) @@ -832,8 +826,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mutex_id = mutex_get_id(this, mutex_op)?; let active_thread = this.get_active_thread(); - release_cond_mutex_and_block(this, active_thread, mutex_id)?; - this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id)); + release_cond_mutex_and_block(this, active_thread, id, mutex_id)?; + this.condvar_wait(id, active_thread, mutex_id); Ok(0) } @@ -866,15 +860,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME") { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) + CallbackTime::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) } else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") { - Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) + CallbackTime::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap()) } else { throw_unsup_format!("unsupported clock id: {}", clock_id); }; - release_cond_mutex_and_block(this, active_thread, mutex_id)?; - this.condvar_wait(id, active_thread, CondvarLock::Mutex(mutex_id)); + release_cond_mutex_and_block(this, active_thread, id, mutex_id)?; + this.condvar_wait(id, active_thread, mutex_id); // We return success for now and override it in the timeout callback. this.write_scalar(Scalar::from_i32(0), dest)?; @@ -897,7 +891,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn call(&self, ecx: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { // We are not waiting for the condvar any more, wait for the // mutex instead. - reacquire_cond_mutex(ecx, self.active_thread, self.mutex_id)?; + reacquire_cond_mutex(ecx, self.active_thread, self.id, self.mutex_id)?; // Remove the thread from the conditional variable. ecx.condvar_remove_waiter(self.id, self.active_thread); diff --git a/src/shims/windows/sync.rs b/src/shims/windows/sync.rs index f02939f888..836b9e9259 100644 --- a/src/shims/windows/sync.rs +++ b/src/shims/windows/sync.rs @@ -170,7 +170,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None } else { let duration = Duration::from_millis(timeout_ms.into()); - Some(Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())) + Some(CallbackTime::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())) }; // See the Linux futex implementation for why this fence exists. @@ -183,7 +183,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if futex_val == compare_val { // If the values are the same, we have to block. - this.block_thread(thread); + this.block_thread(thread, BlockReason::Futex { addr }); this.futex_wait(addr, thread, u32::MAX); if let Some(timeout_time) = timeout_time { @@ -202,7 +202,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { impl<'mir, 'tcx: 'mir> MachineCallback<'mir, 'tcx> for Callback<'tcx> { fn call(&self, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - this.unblock_thread(self.thread); + this.unblock_thread(self.thread, BlockReason::Futex { addr: self.addr }); this.futex_remove_waiter(self.addr, self.thread); let error_timeout = this.eval_windows("c", "ERROR_TIMEOUT"); this.set_last_error(error_timeout)?; @@ -233,8 +233,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See the Linux futex implementation for why this fence exists. this.atomic_fence(AtomicFenceOrd::SeqCst)?; - if let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) { - this.unblock_thread(thread); + let addr = ptr.addr().bytes(); + if let Some(thread) = this.futex_wake(addr, u32::MAX) { + this.unblock_thread(thread, BlockReason::Futex { addr }); this.unregister_timeout_callback_if_exists(thread); } @@ -248,8 +249,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See the Linux futex implementation for why this fence exists. this.atomic_fence(AtomicFenceOrd::SeqCst)?; - while let Some(thread) = this.futex_wake(ptr.addr().bytes(), u32::MAX) { - this.unblock_thread(thread); + let addr = ptr.addr().bytes(); + while let Some(thread) = this.futex_wake(addr, u32::MAX) { + this.unblock_thread(thread, BlockReason::Futex { addr }); this.unregister_timeout_callback_if_exists(thread); } From d87107663e03743e64ba0a2d9b86819910b8d499 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 16 Apr 2024 11:53:15 +0000 Subject: [PATCH 031/208] Miri: adopt to new intrinsic types --- src/shims/intrinsics/simd.rs | 2 +- tests/fail/intrinsics/ctlz_nonzero.rs | 2 +- tests/fail/intrinsics/cttz_nonzero.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index a2fc4f0f76..9a0671430d 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -163,7 +163,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } Op::Numeric(name) => { - this.numeric_intrinsic(name, op.to_scalar(), op.layout)? + this.numeric_intrinsic(name, op.to_scalar(), op.layout, op.layout)? } }; this.write_scalar(val, &dest)?; diff --git a/tests/fail/intrinsics/ctlz_nonzero.rs b/tests/fail/intrinsics/ctlz_nonzero.rs index c26cd4cadb..0b34afc609 100644 --- a/tests/fail/intrinsics/ctlz_nonzero.rs +++ b/tests/fail/intrinsics/ctlz_nonzero.rs @@ -2,7 +2,7 @@ mod rusti { extern "rust-intrinsic" { - pub fn ctlz_nonzero(x: T) -> T; + pub fn ctlz_nonzero(x: T) -> u32; } } diff --git a/tests/fail/intrinsics/cttz_nonzero.rs b/tests/fail/intrinsics/cttz_nonzero.rs index 25a0501fdd..e220411f58 100644 --- a/tests/fail/intrinsics/cttz_nonzero.rs +++ b/tests/fail/intrinsics/cttz_nonzero.rs @@ -2,7 +2,7 @@ mod rusti { extern "rust-intrinsic" { - pub fn cttz_nonzero(x: T) -> T; + pub fn cttz_nonzero(x: T) -> u32; } } From 774e46505453d45732c4ed8c95de1a6c3d3eb6df Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 14:13:18 +0200 Subject: [PATCH 032/208] Box::into_raw: make Miri understand that this is a box-to-raw cast --- .../newtype_pair_retagging.stack.stderr | 2 +- .../newtype_retagging.stack.stderr | 2 +- tests/pass/issues/issue-miri-3473.rs | 28 +++++++++++++++++++ tests/pass/stacked-borrows/stacked-borrows.rs | 12 ++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 tests/pass/issues/issue-miri-3473.rs diff --git a/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr b/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr index 867907e98e..c26c7f397b 100644 --- a/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr +++ b/tests/fail/both_borrows/newtype_pair_retagging.stack.stderr @@ -6,7 +6,7 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information -help: was created by a Unique retag at offsets [0x0..0x4] +help: was created by a SharedReadWrite retag at offsets [0x0..0x4] --> $DIR/newtype_pair_retagging.rs:LL:CC | LL | let ptr = Box::into_raw(Box::new(0i32)); diff --git a/tests/fail/both_borrows/newtype_retagging.stack.stderr b/tests/fail/both_borrows/newtype_retagging.stack.stderr index 56715938e9..ae54da70fe 100644 --- a/tests/fail/both_borrows/newtype_retagging.stack.stderr +++ b/tests/fail/both_borrows/newtype_retagging.stack.stderr @@ -6,7 +6,7 @@ LL | Box(unsafe { Unique::new_unchecked(raw) }, alloc) | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information -help: was created by a Unique retag at offsets [0x0..0x4] +help: was created by a SharedReadWrite retag at offsets [0x0..0x4] --> $DIR/newtype_retagging.rs:LL:CC | LL | let ptr = Box::into_raw(Box::new(0i32)); diff --git a/tests/pass/issues/issue-miri-3473.rs b/tests/pass/issues/issue-miri-3473.rs new file mode 100644 index 0000000000..77b960c1cd --- /dev/null +++ b/tests/pass/issues/issue-miri-3473.rs @@ -0,0 +1,28 @@ +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows +use std::cell::UnsafeCell; + +#[repr(C)] +#[derive(Default)] +struct Node { + _meta: UnsafeCell, + value: usize, +} + +impl Node { + fn value(&self) -> &usize { + &self.value + } +} + +/// This used to cause Stacked Borrows errors because of trouble around conversion +/// from Box to raw pointer. +fn main() { + unsafe { + let a = Box::into_raw(Box::new(Node::default())); + let ptr = &*a; + *UnsafeCell::raw_get(a.cast::>()) = 2; + assert_eq!(*ptr.value(), 0); + drop(Box::from_raw(a)); + } +} diff --git a/tests/pass/stacked-borrows/stacked-borrows.rs b/tests/pass/stacked-borrows/stacked-borrows.rs index 734411ccc7..43ba490d5b 100644 --- a/tests/pass/stacked-borrows/stacked-borrows.rs +++ b/tests/pass/stacked-borrows/stacked-borrows.rs @@ -20,6 +20,7 @@ fn main() { wide_raw_ptr_in_tuple(); not_unpin_not_protected(); write_does_not_invalidate_all_aliases(); + box_into_raw_allows_interior_mutable_alias(); } // Make sure that reading from an `&mut` does, like reborrowing to `&`, @@ -263,3 +264,14 @@ fn write_does_not_invalidate_all_aliases() { other::lib2(); assert_eq!(*x, 1337); // oops, the value changed! I guess not all pointers were invalidated } + +fn box_into_raw_allows_interior_mutable_alias() { unsafe { + let b = Box::new(std::cell::Cell::new(42)); + let raw = Box::into_raw(b); + let c = &*raw; + let d = raw.cast::(); // bypassing `Cell` -- only okay in Miri tests + // `c` and `d` should permit arbitrary aliasing with each other now. + *d = 1; + c.set(2); + drop(Box::from_raw(raw)); +} } From 03b6dae6d2f4893c89f5a87224e4a75238560911 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 13:06:19 +0200 Subject: [PATCH 033/208] deadlock: show backtrace for all threads --- .../stacked_borrows/diagnostics.rs | 2 +- src/concurrency/thread.rs | 5 +- src/diagnostics.rs | 70 +++++++++++++++---- .../fail-dep/concurrency/windows_join_main.rs | 1 + .../concurrency/windows_join_main.stderr | 23 +++++- .../fail-dep/concurrency/windows_join_self.rs | 1 + .../concurrency/windows_join_self.stderr | 24 ++++++- .../shims/sync/libc_pthread_mutex_deadlock.rs | 1 + .../sync/libc_pthread_mutex_deadlock.stderr | 21 +++++- ...libc_pthread_rwlock_write_read_deadlock.rs | 1 + ..._pthread_rwlock_write_read_deadlock.stderr | 21 +++++- ...ibc_pthread_rwlock_write_write_deadlock.rs | 1 + ...pthread_rwlock_write_write_deadlock.stderr | 21 +++++- .../uninit/uninit_alloc_diagnostic.stderr | 4 +- ...it_alloc_diagnostic_with_provenance.stderr | 4 +- 15 files changed, 175 insertions(+), 25 deletions(-) diff --git a/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/borrow_tracker/stacked_borrows/diagnostics.rs index aa99a14b18..5a941ae9d0 100644 --- a/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -438,7 +438,7 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { .machine .threads .all_stacks() - .flatten() + .flat_map(|(_id, stack)| stack) .map(|frame| { frame.extra.borrow_tracker.as_ref().expect("we should have borrow tracking data") }) diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index d1136272f0..6b3dc0fa34 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -430,11 +430,10 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { ) -> &mut Vec>> { &mut self.threads[self.active_thread].stack } - pub fn all_stacks( &self, - ) -> impl Iterator>]> { - self.threads.iter().map(|t| &t.stack[..]) + ) -> impl Iterator>])> { + self.threads.iter_enumerated().map(|(id, t)| (id, &t.stack[..])) } /// Create a new thread and returns its id. diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 30349c003a..dfbcaaac5c 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -361,9 +361,12 @@ pub fn report_error<'tcx, 'mir>( }; let stacktrace = ecx.generate_stacktrace(); - let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); + let (stacktrace, mut any_pruned) = prune_stacktrace(stacktrace, &ecx.machine); - // We want to dump the allocation if this is `InvalidUninitBytes`. Since `format_error` consumes `e`, we compute the outut early. + let mut show_all_threads = false; + + // We want to dump the allocation if this is `InvalidUninitBytes`. + // Since `format_interp_error` consumes `e`, we compute the outut early. let mut extra = String::new(); match e.kind() { UndefinedBehavior(InvalidUninitBytes(Some((alloc_id, access)))) => { @@ -375,6 +378,15 @@ pub fn report_error<'tcx, 'mir>( .unwrap(); writeln!(extra, "{:?}", ecx.dump_alloc(*alloc_id)).unwrap(); } + MachineStop(info) => { + let info = info.downcast_ref::().expect("invalid MachineStop payload"); + match info { + TerminationInfo::Deadlock => { + show_all_threads = true; + } + _ => {} + } + } _ => {} } @@ -387,18 +399,39 @@ pub fn report_error<'tcx, 'mir>( vec![], helps, &stacktrace, + Some(ecx.get_active_thread()), &ecx.machine, ); + eprint!("{extra}"); // newlines are already in the string + + if show_all_threads { + for (thread, stack) in ecx.machine.threads.all_stacks() { + if thread != ecx.get_active_thread() { + let stacktrace = Frame::generate_stacktrace_from_stack(stack); + let (stacktrace, was_pruned) = prune_stacktrace(stacktrace, &ecx.machine); + any_pruned |= was_pruned; + report_msg( + DiagLevel::Error, + format!("deadlock: the evaluated program deadlocked"), + vec![format!("the evaluated program deadlocked")], + vec![], + vec![], + &stacktrace, + Some(thread), + &ecx.machine, + ) + } + } + } + // Include a note like `std` does when we omit frames from a backtrace - if was_pruned { + if any_pruned { ecx.tcx.dcx().note( "some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace", ); } - eprint!("{extra}"); // newlines are already in the string - // Debug-dump all locals. for (i, frame) in ecx.active_thread_stack().iter().enumerate() { trace!("-------------------"); @@ -435,6 +468,7 @@ pub fn report_leaks<'mir, 'tcx>( vec![], vec![], &backtrace, + None, // we don't know the thread this is from &ecx.machine, ); } @@ -457,6 +491,7 @@ pub fn report_msg<'tcx>( notes: Vec<(Option, String)>, helps: Vec<(Option, String)>, stacktrace: &[FrameInfo<'tcx>], + thread: Option, machine: &MiriMachine<'_, 'tcx>, ) { let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span); @@ -506,12 +541,13 @@ pub fn report_msg<'tcx>( if extra_span { write!(backtrace_title, " (of the first span)").unwrap(); } - let thread_name = - machine.threads.get_thread_display_name(machine.threads.get_active_thread_id()); - if thread_name != "main" { - // Only print thread name if it is not `main`. - write!(backtrace_title, " on thread `{thread_name}`").unwrap(); - }; + if let Some(thread) = thread { + let thread_name = machine.threads.get_thread_display_name(thread); + if thread_name != "main" { + // Only print thread name if it is not `main`. + write!(backtrace_title, " on thread `{thread_name}`").unwrap(); + }; + } write!(backtrace_title, ":").unwrap(); err.note(backtrace_title); for (idx, frame_info) in stacktrace.iter().enumerate() { @@ -628,7 +664,16 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { _ => vec![], }; - report_msg(diag_level, title, vec![msg], notes, helps, &stacktrace, self); + report_msg( + diag_level, + title, + vec![msg], + notes, + helps, + &stacktrace, + Some(self.threads.get_active_thread_id()), + self, + ); } } @@ -654,6 +699,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { vec![], vec![], &stacktrace, + Some(this.get_active_thread()), &this.machine, ); } diff --git a/tests/fail-dep/concurrency/windows_join_main.rs b/tests/fail-dep/concurrency/windows_join_main.rs index 910e06222e..532bda2013 100644 --- a/tests/fail-dep/concurrency/windows_join_main.rs +++ b/tests/fail-dep/concurrency/windows_join_main.rs @@ -1,6 +1,7 @@ //@only-target-windows: Uses win32 api functions // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 +//@error-in-other-file: deadlock // On windows, joining main is not UB, but it will block a thread forever. diff --git a/tests/fail-dep/concurrency/windows_join_main.stderr b/tests/fail-dep/concurrency/windows_join_main.stderr index d9137ee743..12f35fdeb0 100644 --- a/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/tests/fail-dep/concurrency/windows_join_main.stderr @@ -8,7 +8,28 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ = note: inside closure at RUSTLIB/core/src/macros/mod.rs:LL:CC = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/windows_join_main.rs:LL:CC + | +LL | / thread::spawn(|| { +LL | | unsafe { +LL | | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJECT_0); +LL | | } +LL | | }) +LL | | .join() + | |___________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/fail-dep/concurrency/windows_join_self.rs b/tests/fail-dep/concurrency/windows_join_self.rs index a7c8faf5a9..a64265ca0c 100644 --- a/tests/fail-dep/concurrency/windows_join_self.rs +++ b/tests/fail-dep/concurrency/windows_join_self.rs @@ -1,6 +1,7 @@ //@only-target-windows: Uses win32 api functions // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 +//@error-in-other-file: deadlock // On windows, a thread joining itself is not UB, but it will deadlock. diff --git a/tests/fail-dep/concurrency/windows_join_self.stderr b/tests/fail-dep/concurrency/windows_join_self.stderr index 74699a0317..8d26c35de8 100644 --- a/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/tests/fail-dep/concurrency/windows_join_self.stderr @@ -7,7 +7,29 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0 = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/windows_join_self.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/windows_join_self.rs:LL:CC + | +LL | / thread::spawn(|| { +LL | | unsafe { +LL | | let native = GetCurrentThread(); +LL | | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0); +LL | | } +LL | | }) +LL | | .join() + | |___________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs index 6c3cb738e2..60d56d41fd 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs +++ b/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@error-in-other-file: deadlock use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr index 76b1d26bd3..987d0fc4c2 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr +++ b/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr @@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _ = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/libc_pthread_mutex_deadlock.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC + | +LL | / thread::spawn(move || { +LL | | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0); +LL | | }) +LL | | .join() + | |_______________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs index 201844615e..0f02c3231a 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs +++ b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@error-in-other-file: deadlock use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr index 5501dab81a..bc9b15f293 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr @@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC + | +LL | / thread::spawn(move || { +LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); +LL | | }) +LL | | .join() + | |_______________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs index b1d7e0492e..10be5b3375 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs +++ b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@error-in-other-file: deadlock use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr index 815d85af50..66c142bbc5 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr @@ -7,7 +7,26 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC +error: deadlock: the evaluated program deadlocked + --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + | +LL | let ret = libc::pthread_join(self.id, ptr::null_mut()); + | ^ the evaluated program deadlocked + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC + = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC +note: inside `main` + --> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC + | +LL | / thread::spawn(move || { +LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0); +LL | | }) +LL | | .join() + | |_______________^ + note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error +error: aborting due to 2 previous errors diff --git a/tests/fail/uninit/uninit_alloc_diagnostic.stderr b/tests/fail/uninit/uninit_alloc_diagnostic.stderr index cca17a07ec..960cae9012 100644 --- a/tests/fail/uninit/uninit_alloc_diagnostic.stderr +++ b/tests/fail/uninit/uninit_alloc_diagnostic.stderr @@ -15,13 +15,13 @@ note: inside `main` LL | drop(slice1.cmp(slice2)); | ^^^^^^^^^^^^^^^^^^ -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - Uninitialized memory occurred at ALLOC[0x4..0x10], in this allocation: ALLOC (Rust heap, size: 32, align: 8) { 0x00 │ 41 42 43 44 __ __ __ __ __ __ __ __ __ __ __ __ │ ABCD░░░░░░░░░░░░ 0x10 │ 00 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ .░░░░░░░░░░░░░░░ } +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + error: aborting due to 1 previous error diff --git a/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr index 4dc2d27ead..5439418f26 100644 --- a/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr +++ b/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr @@ -15,8 +15,6 @@ note: inside `main` LL | drop(slice1.cmp(slice2)); | ^^^^^^^^^^^^^^^^^^ -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation: ALLOC (Rust heap, size: 16, align: 8) { ╾42[ALLOC] (1 ptr byte)╼ 12 13 ╾43[ALLOC] (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░ @@ -28,5 +26,7 @@ ALLOC (global (static or const), size: 1, align: 1) { 00 │ . } +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + error: aborting due to 1 previous error From 2962277b2077200483de0047d087c3855d8330f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 16:37:34 +0200 Subject: [PATCH 034/208] interpret: pass MemoryKind to before_memory_deallocation --- src/borrow_tracker/mod.rs | 2 +- src/borrow_tracker/stacked_borrows/mod.rs | 2 +- src/borrow_tracker/tree_borrows/mod.rs | 2 +- src/concurrency/data_race.rs | 2 +- src/diagnostics.rs | 4 ++-- src/lib.rs | 2 +- src/machine.rs | 15 ++++++++------- src/shims/os_str.rs | 8 ++++---- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/borrow_tracker/mod.rs b/src/borrow_tracker/mod.rs index 8d76a48826..f21315790a 100644 --- a/src/borrow_tracker/mod.rs +++ b/src/borrow_tracker/mod.rs @@ -260,7 +260,7 @@ impl GlobalStateInner { &mut self, id: AllocId, alloc_size: Size, - kind: MemoryKind, + kind: MemoryKind, machine: &MiriMachine<'_, '_>, ) -> AllocState { match self.borrow_tracker_method { diff --git a/src/borrow_tracker/stacked_borrows/mod.rs b/src/borrow_tracker/stacked_borrows/mod.rs index 96ff298402..b4005515d9 100644 --- a/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/borrow_tracker/stacked_borrows/mod.rs @@ -509,7 +509,7 @@ impl Stacks { id: AllocId, size: Size, state: &mut GlobalStateInner, - kind: MemoryKind, + kind: MemoryKind, machine: &MiriMachine<'_, '_>, ) -> Self { let (base_tag, perm) = match kind { diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index a3d49756e4..492e324de4 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -34,7 +34,7 @@ impl<'tcx> Tree { id: AllocId, size: Size, state: &mut GlobalStateInner, - _kind: MemoryKind, + _kind: MemoryKind, machine: &MiriMachine<'_, 'tcx>, ) -> Self { let tag = state.base_ptr_tag(id, machine); // Fresh tag for the root diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index d51160b283..95049b91cb 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -844,7 +844,7 @@ impl VClockAlloc { global: &GlobalState, thread_mgr: &ThreadManager<'_, '_>, len: Size, - kind: MemoryKind, + kind: MemoryKind, current_span: Span, ) -> VClockAlloc { let (alloc_timestamp, alloc_index) = match kind { diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 30349c003a..a2b817ea0d 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -115,7 +115,7 @@ pub enum NonHaltingDiagnostic { /// This `Item` was popped from the borrow stack. The string explains the reason. PoppedPointerTag(Item, String), CreatedCallId(CallId), - CreatedAlloc(AllocId, Size, Align, MemoryKind), + CreatedAlloc(AllocId, Size, Align, MemoryKind), FreedAlloc(AllocId), AccessedAlloc(AllocId, AccessKind), RejectedIsolatedOp(String), @@ -414,7 +414,7 @@ pub fn report_error<'tcx, 'mir>( pub fn report_leaks<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - leaks: Vec<(AllocId, MemoryKind, Allocation>)>, + leaks: Vec<(AllocId, MemoryKind, Allocation>)>, ) { let mut any_pruned = false; for (id, kind, mut alloc) in leaks { diff --git a/src/lib.rs b/src/lib.rs index 7821aa9efd..42e66057b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -125,7 +125,7 @@ pub use crate::eval::{ }; pub use crate::helpers::{AccessKind, EvalContextExt as _}; pub use crate::machine::{ - AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, + AllocExtra, FrameExtra, MemoryKind, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra, }; pub use crate::mono_hash_map::MonoHashMap; diff --git a/src/machine.rs b/src/machine.rs index ff081328a7..1d06d5c69d 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -135,9 +135,9 @@ pub enum MiriMemoryKind { Mmap, } -impl From for MemoryKind { +impl From for MemoryKind { #[inline(always)] - fn from(kind: MiriMemoryKind) -> MemoryKind { + fn from(kind: MiriMemoryKind) -> MemoryKind { MemoryKind::Machine(kind) } } @@ -185,6 +185,8 @@ impl fmt::Display for MiriMemoryKind { } } +pub type MemoryKind = interpret::MemoryKind; + /// Pointer provenance. #[derive(Clone, Copy)] pub enum Provenance { @@ -863,10 +865,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { type ProvenanceExtra = ProvenanceExtra; type Bytes = Box<[u8]>; - type MemoryMap = MonoHashMap< - AllocId, - (MemoryKind, Allocation), - >; + type MemoryMap = + MonoHashMap)>; const GLOBAL_KIND: Option = Some(MiriMemoryKind::Global); @@ -1088,7 +1088,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { ecx: &MiriInterpCx<'mir, 'tcx>, id: AllocId, alloc: Cow<'b, Allocation>, - kind: Option>, + kind: Option, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { @@ -1280,6 +1280,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra), size: Size, align: Align, + _kind: MemoryKind, ) -> InterpResult<'tcx> { if machine.tracked_alloc_ids.contains(&alloc_id) { machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 62ce2ee58a..a27c9b746d 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -136,7 +136,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn alloc_os_str_as_c_str( &mut self, os_str: &OsStr, - memkind: MemoryKind, + memkind: MemoryKind, ) -> InterpResult<'tcx, Pointer>> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. let this = self.eval_context_mut(); @@ -152,7 +152,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn alloc_os_str_as_wide_str( &mut self, os_str: &OsStr, - memkind: MemoryKind, + memkind: MemoryKind, ) -> InterpResult<'tcx, Pointer>> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); @@ -229,7 +229,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn alloc_path_as_c_str( &mut self, path: &Path, - memkind: MemoryKind, + memkind: MemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); let os_str = @@ -242,7 +242,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn alloc_path_as_wide_str( &mut self, path: &Path, - memkind: MemoryKind, + memkind: MemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); let os_str = From 3b9ec8e0d92d87c7b3e73c4ec60de0d5dfd67a9b Mon Sep 17 00:00:00 2001 From: zetanumbers Date: Tue, 13 Feb 2024 12:31:41 +0300 Subject: [PATCH 035/208] Add simple async drop glue generation Explainer: https://zetanumbers.github.io/book/async-drop-design.html https://github.com/rust-lang/rust/pull/121801 --- tests/pass/async-drop.rs | 191 +++++++++++++++++++++++++++++ tests/pass/async-drop.stack.stdout | 22 ++++ tests/pass/async-drop.tree.stdout | 22 ++++ 3 files changed, 235 insertions(+) create mode 100644 tests/pass/async-drop.rs create mode 100644 tests/pass/async-drop.stack.stdout create mode 100644 tests/pass/async-drop.tree.stdout diff --git a/tests/pass/async-drop.rs b/tests/pass/async-drop.rs new file mode 100644 index 0000000000..f16206f3db --- /dev/null +++ b/tests/pass/async-drop.rs @@ -0,0 +1,191 @@ +//@revisions: stack tree +//@compile-flags: -Zmiri-strict-provenance +//@[tree]compile-flags: -Zmiri-tree-borrows +#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)] +#![allow(incomplete_features, dead_code)] + +// FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests +use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::hint::black_box; +use core::mem::{self, ManuallyDrop}; +use core::pin::{pin, Pin}; +use core::task::{Context, Poll, Waker}; + +async fn test_async_drop(x: T) { + let mut x = mem::MaybeUninit::new(x); + let dtor = pin!(unsafe { async_drop_in_place(x.as_mut_ptr()) }); + test_idempotency(dtor).await; +} + +fn test_idempotency(mut x: Pin<&mut T>) -> impl Future + '_ +where + T: Future, +{ + core::future::poll_fn(move |cx| { + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + assert_eq!(x.as_mut().poll(cx), Poll::Ready(())); + Poll::Ready(()) + }) +} + +fn main() { + let waker = Waker::noop(); + let mut cx = Context::from_waker(&waker); + + let i = 13; + let fut = pin!(async { + test_async_drop(Int(0)).await; + test_async_drop(AsyncInt(0)).await; + test_async_drop([AsyncInt(1), AsyncInt(2)]).await; + test_async_drop((AsyncInt(3), AsyncInt(4))).await; + test_async_drop(5).await; + let j = 42; + test_async_drop(&i).await; + test_async_drop(&j).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9))).await; + + let foo = AsyncInt(10); + test_async_drop(AsyncReference { foo: &foo }).await; + + let foo = AsyncInt(11); + test_async_drop(|| { + black_box(foo); + let foo = AsyncInt(10); + foo + }) + .await; + + test_async_drop(AsyncEnum::A(AsyncInt(12))).await; + test_async_drop(AsyncEnum::B(SyncInt(13))).await; + + test_async_drop(SyncInt(14)).await; + test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }) + .await; + + let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + test_idempotency(async_drop_fut).await; + + let foo = AsyncInt(20); + test_async_drop(async || { + black_box(foo); + let foo = AsyncInt(19); + // Await point there, but this is async closure so it's fine + black_box(core::future::ready(())).await; + foo + }) + .await; + + test_async_drop(AsyncUnion { signed: 21 }).await; + }); + let res = fut.poll(&mut cx); + assert_eq!(res, Poll::Ready(())); +} + +struct AsyncInt(i32); + +impl AsyncDrop for AsyncInt { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncInt::Dropper::poll: {}", self.0); + } + } +} + +struct SyncInt(i32); + +impl Drop for SyncInt { + fn drop(&mut self) { + println!("SyncInt::drop: {}", self.0); + } +} + +struct SyncThenAsync { + i: i32, + a: AsyncInt, + b: SyncInt, + c: AsyncInt, +} + +impl Drop for SyncThenAsync { + fn drop(&mut self) { + println!("SyncThenAsync::drop: {}", self.i); + } +} + +struct AsyncReference<'a> { + foo: &'a AsyncInt, +} + +impl AsyncDrop for AsyncReference<'_> { + type Dropper<'a> = impl Future where Self: 'a; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); + } + } +} + +struct Int(i32); + +struct AsyncStruct { + i: i32, + a: AsyncInt, + b: AsyncInt, +} + +impl AsyncDrop for AsyncStruct { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncStruct::Dropper::poll: {}", self.i); + } + } +} + +enum AsyncEnum { + A(AsyncInt), + B(SyncInt), +} + +impl AsyncDrop for AsyncEnum { + type Dropper<'a> = impl Future; + + fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } + } +} + +// FIXME(zetanumbers): Disallow types with `AsyncDrop` in unions +union AsyncUnion { + signed: i32, + unsigned: u32, +} + +impl AsyncDrop for AsyncUnion { + type Dropper<'a> = impl Future; + + fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { + async move { + println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe { + self.unsigned + }); + } + } +} diff --git a/tests/pass/async-drop.stack.stdout b/tests/pass/async-drop.stack.stdout new file mode 100644 index 0000000000..9cae4331ca --- /dev/null +++ b/tests/pass/async-drop.stack.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 diff --git a/tests/pass/async-drop.tree.stdout b/tests/pass/async-drop.tree.stdout new file mode 100644 index 0000000000..9cae4331ca --- /dev/null +++ b/tests/pass/async-drop.tree.stdout @@ -0,0 +1,22 @@ +AsyncInt::Dropper::poll: 0 +AsyncInt::Dropper::poll: 1 +AsyncInt::Dropper::poll: 2 +AsyncInt::Dropper::poll: 3 +AsyncInt::Dropper::poll: 4 +AsyncStruct::Dropper::poll: 6 +AsyncInt::Dropper::poll: 7 +AsyncInt::Dropper::poll: 8 +AsyncReference::Dropper::poll: 10 +AsyncInt::Dropper::poll: 11 +AsyncEnum(A)::Dropper::poll: 12 +SyncInt::drop: 12 +AsyncEnum(B)::Dropper::poll: 13 +AsyncInt::Dropper::poll: 13 +SyncInt::drop: 14 +SyncThenAsync::drop: 15 +AsyncInt::Dropper::poll: 16 +SyncInt::drop: 17 +AsyncInt::Dropper::poll: 18 +AsyncInt::Dropper::poll: 19 +AsyncInt::Dropper::poll: 20 +AsyncUnion::Dropper::poll: 21, 21 From 1b48cec5a64342d7758b5e40841e8f839010cd3a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 21:46:18 +0200 Subject: [PATCH 036/208] no_std works on Windows now --- tests/fail/panic/no_std.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/fail/panic/no_std.rs b/tests/fail/panic/no_std.rs index bad425804d..26cc0b2782 100644 --- a/tests/fail/panic/no_std.rs +++ b/tests/fail/panic/no_std.rs @@ -1,10 +1,6 @@ #![feature(start, core_intrinsics)] #![no_std] //@compile-flags: -Cpanic=abort -// windows tls dtors go through libstd right now, thus this test -// cannot pass. When windows tls dtors go through the special magic -// windows linker section, we can run this test on windows again. -//@ignore-target-windows: no-std not supported on Windows // Plumbing to let us use `writeln!` to host stderr: From 150a4e9365036784217e7176455763d81db1ee74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 21:59:19 +0200 Subject: [PATCH 037/208] implement support for __rust_alloc_error_handler --- src/shims/extern_static.rs | 7 +- src/shims/foreign_items.rs | 16 ++++- tests/fail/alloc/alloc_error_handler.rs | 25 +++++++ tests/fail/alloc/alloc_error_handler.stderr | 26 ++++++++ .../fail/alloc/alloc_error_handler_no_std.rs | 65 +++++++++++++++++++ .../alloc/alloc_error_handler_no_std.stderr | 29 +++++++++ tests/panic/alloc_error_handler_panic.rs | 32 +++++++++ tests/panic/alloc_error_handler_panic.stderr | 4 ++ tests/pass/alloc-access-tracking.rs | 2 +- tests/pass/alloc-access-tracking.stderr | 8 +-- 10 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 tests/fail/alloc/alloc_error_handler.rs create mode 100644 tests/fail/alloc/alloc_error_handler.stderr create mode 100644 tests/fail/alloc/alloc_error_handler_no_std.rs create mode 100644 tests/fail/alloc/alloc_error_handler_no_std.stderr create mode 100644 tests/panic/alloc_error_handler_panic.rs create mode 100644 tests/panic/alloc_error_handler_panic.stderr diff --git a/src/shims/extern_static.rs b/src/shims/extern_static.rs index 0284e5b606..7c4a54fb46 100644 --- a/src/shims/extern_static.rs +++ b/src/shims/extern_static.rs @@ -32,9 +32,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// Sets up the "extern statics" for this machine. pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { // "__rust_no_alloc_shim_is_unstable" - let val = ImmTy::from_int(0, this.machine.layouts.u8); + let val = ImmTy::from_int(0, this.machine.layouts.u8); // always 0, value does not matter Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?; + // "__rust_alloc_error_handler_should_panic" + let val = this.tcx.sess.opts.unstable_opts.oom.should_panic(); + let val = ImmTy::from_int(val, this.machine.layouts.u8); + Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?; + match this.tcx.sess.target.os.as_ref() { "linux" => { Self::null_ptr_extern_statics( diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 6b0797f6da..e6fc29a5ae 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -1,7 +1,7 @@ use std::{collections::hash_map::Entry, io::Write, iter, path::Path}; use rustc_apfloat::Float; -use rustc_ast::expand::allocator::AllocatorKind; +use rustc_ast::expand::allocator::{alloc_error_handler_name, AllocatorKind}; use rustc_hir::{def::DefKind, def_id::CrateNum}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; @@ -80,6 +80,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { panic_impl_instance, ))); } + "__rust_alloc_error_handler" => { + // Forward to the right symbol that implements this function. + let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { + // in real code, this symbol does not exist without an allocator + throw_unsup_format!( + "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" + ); + }; + let name = alloc_error_handler_name(handler_kind); + let handler = this + .lookup_exported_symbol(Symbol::intern(name))? + .expect("missing alloc error handler symbol"); + return Ok(Some(handler)); + } #[rustfmt::skip] | "exit" | "ExitProcess" diff --git a/tests/fail/alloc/alloc_error_handler.rs b/tests/fail/alloc/alloc_error_handler.rs new file mode 100644 index 0000000000..dc8e8c7380 --- /dev/null +++ b/tests/fail/alloc/alloc_error_handler.rs @@ -0,0 +1,25 @@ +//@error-in-other-file: aborted +//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();" +//@normalize-stderr-test: "\| +\^+" -> "| ^" +#![feature(allocator_api)] + +use std::alloc::*; +use std::ptr::NonNull; + +struct BadAlloc; + +// Create a failing allocator; Miri's native allocator never fails so this is the only way to +// actually call the alloc error handler. +unsafe impl Allocator for BadAlloc { + fn allocate(&self, _l: Layout) -> Result, AllocError> { + Err(AllocError) + } + + unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { + unreachable!(); + } +} + +fn main() { + let _b = Box::new_in(0, BadAlloc); +} diff --git a/tests/fail/alloc/alloc_error_handler.stderr b/tests/fail/alloc/alloc_error_handler.stderr new file mode 100644 index 0000000000..f9d8e80c0f --- /dev/null +++ b/tests/fail/alloc/alloc_error_handler.stderr @@ -0,0 +1,26 @@ +memory allocation of 4 bytes failed +error: abnormal termination: the program aborted execution + --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + | +LL | ABORT(); + | ^ the program aborted execution + | + = note: BACKTRACE: + = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC + = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC + = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `std::boxed::Box::::new_uninit_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC + = note: inside `std::boxed::Box::::new_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `main` + --> $DIR/alloc_error_handler.rs:LL:CC + | +LL | let _b = Box::new_in(0, BadAlloc); + | ^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/fail/alloc/alloc_error_handler_no_std.rs b/tests/fail/alloc/alloc_error_handler_no_std.rs new file mode 100644 index 0000000000..8103296f47 --- /dev/null +++ b/tests/fail/alloc/alloc_error_handler_no_std.rs @@ -0,0 +1,65 @@ +//@compile-flags: -Cpanic=abort +#![feature(start, core_intrinsics)] +#![feature(alloc_error_handler)] +#![feature(allocator_api)] +#![no_std] + +extern crate alloc; + +use alloc::alloc::*; +use alloc::boxed::Box; +use core::ptr::NonNull; + +struct BadAlloc; + +// Create a failing allocator; that is the only way to actually call the alloc error handler. +unsafe impl Allocator for BadAlloc { + fn allocate(&self, _l: Layout) -> Result, AllocError> { + Err(AllocError) + } + + unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { + unreachable!(); + } +} + +#[alloc_error_handler] +fn alloc_error_handler(_: Layout) -> ! { + extern "Rust" { + fn miri_write_to_stderr(bytes: &[u8]); + } + let msg = "custom alloc error handler called!\n"; + unsafe { miri_write_to_stderr(msg.as_bytes()) }; + core::intrinsics::abort(); //~ERROR: aborted +} + +// rustc requires us to provide some more things that aren't actually used by this test +mod plumbing { + use super::*; + + #[panic_handler] + fn panic_handler(_: &core::panic::PanicInfo) -> ! { + core::intrinsics::abort(); + } + + struct NoAlloc; + + unsafe impl GlobalAlloc for NoAlloc { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { + unreachable!(); + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + unreachable!(); + } + } + + #[global_allocator] + static GLOBAL: NoAlloc = NoAlloc; +} + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + let _b = Box::new_in(0, BadAlloc); + 0 +} diff --git a/tests/fail/alloc/alloc_error_handler_no_std.stderr b/tests/fail/alloc/alloc_error_handler_no_std.stderr new file mode 100644 index 0000000000..b40ffb7012 --- /dev/null +++ b/tests/fail/alloc/alloc_error_handler_no_std.stderr @@ -0,0 +1,29 @@ +custom alloc error handler called! +error: abnormal termination: the program aborted execution + --> $DIR/alloc_error_handler_no_std.rs:LL:CC + | +LL | core::intrinsics::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution + | + = note: BACKTRACE: + = note: inside `alloc_error_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC +note: inside `_::__rg_oom` + --> $DIR/alloc_error_handler_no_std.rs:LL:CC + | +LL | #[alloc_error_handler] + | ---------------------- in this procedural macro expansion +LL | fn alloc_error_handler(_: Layout) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::boxed::Box::::new_uninit_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC + = note: inside `alloc::boxed::Box::::new_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `start` + --> $DIR/alloc_error_handler_no_std.rs:LL:CC + | +LL | let _b = Box::new_in(0, BadAlloc); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/panic/alloc_error_handler_panic.rs b/tests/panic/alloc_error_handler_panic.rs new file mode 100644 index 0000000000..186c9667a9 --- /dev/null +++ b/tests/panic/alloc_error_handler_panic.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zoom=panic +#![feature(allocator_api)] + +use std::alloc::*; +use std::ptr::NonNull; + +struct BadAlloc; + +// Create a failing allocator; Miri's native allocator never fails so this is the only way to +// actually call the alloc error handler. +unsafe impl Allocator for BadAlloc { + fn allocate(&self, _l: Layout) -> Result, AllocError> { + Err(AllocError) + } + + unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { + unreachable!(); + } +} + +struct Bomb; +impl Drop for Bomb { + fn drop(&mut self) { + eprintln!("yes we are unwinding!"); + } +} + +fn main() { + let bomb = Bomb; + let _b = Box::new_in(0, BadAlloc); + std::mem::forget(bomb); // defuse unwinding bomb +} diff --git a/tests/panic/alloc_error_handler_panic.stderr b/tests/panic/alloc_error_handler_panic.stderr new file mode 100644 index 0000000000..202325468b --- /dev/null +++ b/tests/panic/alloc_error_handler_panic.stderr @@ -0,0 +1,4 @@ +thread 'main' panicked at RUSTLIB/std/src/alloc.rs:LL:CC: +memory allocation of 4 bytes failed +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +yes we are unwinding! diff --git a/tests/pass/alloc-access-tracking.rs b/tests/pass/alloc-access-tracking.rs index 5c782fca2d..29c1ee2f7b 100644 --- a/tests/pass/alloc-access-tracking.rs +++ b/tests/pass/alloc-access-tracking.rs @@ -1,6 +1,6 @@ #![feature(start)] #![no_std] -//@compile-flags: -Zmiri-track-alloc-id=17 -Zmiri-track-alloc-accesses -Cpanic=abort +//@compile-flags: -Zmiri-track-alloc-id=18 -Zmiri-track-alloc-accesses -Cpanic=abort //@only-target-linux: alloc IDs differ between OSes for some reason extern "Rust" { diff --git a/tests/pass/alloc-access-tracking.stderr b/tests/pass/alloc-access-tracking.stderr index 5e219fa1be..bef13701ea 100644 --- a/tests/pass/alloc-access-tracking.stderr +++ b/tests/pass/alloc-access-tracking.stderr @@ -2,7 +2,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | let ptr = miri_alloc(123, 1); - | ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 17 + | ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 18 | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC @@ -11,7 +11,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | *ptr = 42; // Crucially, only a write is printed here, no read! - | ^^^^^^^^^ write access to allocation with id 17 + | ^^^^^^^^^ write access to allocation with id 18 | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC @@ -20,7 +20,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | assert_eq!(*ptr, 42); - | ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 17 + | ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 18 | = note: BACKTRACE: = note: inside `start` at RUSTLIB/core/src/macros/mod.rs:LL:CC @@ -30,7 +30,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | miri_dealloc(ptr, 123, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 17 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 18 | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC From b5d69930949cb829a80fcea8d22e80ac3f235c20 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 17 Apr 2024 04:57:09 +0000 Subject: [PATCH 038/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index dfa7f8ca50..bd87405da3 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -63f70b3d104e20289a1a0df82747066c3d85b9a1 +803e33a4460c82581bd01d4008d0f44aef1ddfe8 From 3f2d17ca5f9d08a4664df5905da8463f055333d7 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 17 Apr 2024 05:09:14 +0000 Subject: [PATCH 039/208] fmt --- tests/pass/stacked-borrows/stacked-borrows.rs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/pass/stacked-borrows/stacked-borrows.rs b/tests/pass/stacked-borrows/stacked-borrows.rs index 43ba490d5b..c75824d7f9 100644 --- a/tests/pass/stacked-borrows/stacked-borrows.rs +++ b/tests/pass/stacked-borrows/stacked-borrows.rs @@ -265,13 +265,15 @@ fn write_does_not_invalidate_all_aliases() { assert_eq!(*x, 1337); // oops, the value changed! I guess not all pointers were invalidated } -fn box_into_raw_allows_interior_mutable_alias() { unsafe { - let b = Box::new(std::cell::Cell::new(42)); - let raw = Box::into_raw(b); - let c = &*raw; - let d = raw.cast::(); // bypassing `Cell` -- only okay in Miri tests - // `c` and `d` should permit arbitrary aliasing with each other now. - *d = 1; - c.set(2); - drop(Box::from_raw(raw)); -} } +fn box_into_raw_allows_interior_mutable_alias() { + unsafe { + let b = Box::new(std::cell::Cell::new(42)); + let raw = Box::into_raw(b); + let c = &*raw; + let d = raw.cast::(); // bypassing `Cell` -- only okay in Miri tests + // `c` and `d` should permit arbitrary aliasing with each other now. + *d = 1; + c.set(2); + drop(Box::from_raw(raw)); + } +} From b3e80877d1baf4313233d4a8a7e84d3a1b1d32b3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 18:03:05 +0200 Subject: [PATCH 040/208] interpret: pass MemoryKind to adjust_alloc_base_pointer --- src/alloc_addresses/mod.rs | 15 ++++++++++----- src/machine.rs | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index fec39ec2b8..22f9a90a80 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -141,7 +141,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn addr_from_alloc_id(&self, alloc_id: AllocId) -> InterpResult<'tcx, u64> { + fn addr_from_alloc_id( + &self, + alloc_id: AllocId, + _kind: MemoryKind, + ) -> InterpResult<'tcx, u64> { let ecx = self.eval_context_ref(); let mut global_state = ecx.machine.alloc_addresses.borrow_mut(); let global_state = &mut *global_state; @@ -283,16 +287,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Convert a relative (tcx) pointer to a Miri pointer. - fn ptr_from_rel_ptr( + fn adjust_alloc_base_pointer( &self, ptr: Pointer, tag: BorTag, + kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { let ecx = self.eval_context_ref(); let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance) let alloc_id = prov.alloc_id(); - let base_addr = ecx.addr_from_alloc_id(alloc_id)?; + let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?; // Add offset with the right kind of pointer-overflowing arithmetic. let dl = ecx.data_layout(); @@ -314,9 +319,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ecx.alloc_id_from_addr(addr.bytes())? }; - // This cannot fail: since we already have a pointer with that provenance, rel_ptr_to_addr + // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_base_pointer // must have been called in the past, so we can just look up the address in the map. - let base_addr = ecx.addr_from_alloc_id(alloc_id).unwrap(); + let base_addr = *ecx.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap(); // Wrapping "addr - base_addr" #[allow(clippy::cast_possible_wrap)] // we want to wrap here diff --git a/src/machine.rs b/src/machine.rs index 1d06d5c69d..2bd1b24ee9 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1090,7 +1090,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { alloc: Cow<'b, Allocation>, kind: Option, ) -> InterpResult<'tcx, Cow<'b, Allocation>> { - let kind = kind.expect("we set our STATIC_KIND so this cannot be None"); + let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None"); if ecx.machine.tracked_alloc_ids.contains(&id) { ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc( id, @@ -1151,7 +1151,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { fn adjust_alloc_base_pointer( ecx: &MiriInterpCx<'mir, 'tcx>, ptr: Pointer, + kind: Option, ) -> InterpResult<'tcx, Pointer> { + let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None"); let alloc_id = ptr.provenance.alloc_id(); if cfg!(debug_assertions) { // The machine promises to never call us on thread-local or extern statics. @@ -1172,7 +1174,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { // Value does not matter, SB is disabled BorTag::default() }; - ecx.ptr_from_rel_ptr(ptr, tag) + ecx.adjust_alloc_base_pointer(ptr, tag, kind) } /// Called on `usize as ptr` casts. From 444a4401b6d1024ab9c6f8eb99dc086144ef9851 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 18:37:56 +0200 Subject: [PATCH 041/208] interpret: rename base_pointer -> root_pointer also in Miri, "base tag" -> "root tag" --- src/alloc_addresses/mod.rs | 4 ++-- src/borrow_tracker/mod.rs | 18 +++++++++--------- .../stacked_borrows/diagnostics.rs | 12 ++++++------ src/borrow_tracker/stacked_borrows/mod.rs | 4 ++-- src/borrow_tracker/stacked_borrows/stack.rs | 10 +++++----- src/borrow_tracker/tree_borrows/mod.rs | 2 +- src/machine.rs | 18 +++++++++--------- .../invalidate_against_protector3.stack.stderr | 2 +- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 22f9a90a80..b4983656ad 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -287,7 +287,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Convert a relative (tcx) pointer to a Miri pointer. - fn adjust_alloc_base_pointer( + fn adjust_alloc_root_pointer( &self, ptr: Pointer, tag: BorTag, @@ -319,7 +319,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ecx.alloc_id_from_addr(addr.bytes())? }; - // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_base_pointer + // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer // must have been called in the past, so we can just look up the address in the map. let base_addr = *ecx.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap(); diff --git a/src/borrow_tracker/mod.rs b/src/borrow_tracker/mod.rs index f21315790a..24e2a9a74b 100644 --- a/src/borrow_tracker/mod.rs +++ b/src/borrow_tracker/mod.rs @@ -89,10 +89,10 @@ pub struct GlobalStateInner { borrow_tracker_method: BorrowTrackerMethod, /// Next unused pointer ID (tag). next_ptr_tag: BorTag, - /// Table storing the "base" tag for each allocation. - /// The base tag is the one used for the initial pointer. + /// Table storing the "root" tag for each allocation. + /// The root tag is the one used for the initial pointer. /// We need this in a separate table to handle cyclic statics. - base_ptr_tags: FxHashMap, + root_ptr_tags: FxHashMap, /// Next unused call ID (for protectors). next_call_id: CallId, /// All currently protected tags. @@ -175,7 +175,7 @@ impl GlobalStateInner { GlobalStateInner { borrow_tracker_method, next_ptr_tag: BorTag::one(), - base_ptr_tags: FxHashMap::default(), + root_ptr_tags: FxHashMap::default(), next_call_id: NonZero::new(1).unwrap(), protected_tags: FxHashMap::default(), tracked_pointer_tags, @@ -213,8 +213,8 @@ impl GlobalStateInner { } } - pub fn base_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag { - self.base_ptr_tags.get(&id).copied().unwrap_or_else(|| { + pub fn root_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_, '_>) -> BorTag { + self.root_ptr_tags.get(&id).copied().unwrap_or_else(|| { let tag = self.new_ptr(); if self.tracked_pointer_tags.contains(&tag) { machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag( @@ -223,14 +223,14 @@ impl GlobalStateInner { None, )); } - trace!("New allocation {:?} has base tag {:?}", id, tag); - self.base_ptr_tags.try_insert(id, tag).unwrap(); + trace!("New allocation {:?} has rpot tag {:?}", id, tag); + self.root_ptr_tags.try_insert(id, tag).unwrap(); tag }) } pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_, '_>) { - self.base_ptr_tags.retain(|id, _| allocs.is_live(*id)); + self.root_ptr_tags.retain(|id, _| allocs.is_live(*id)); } } diff --git a/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/borrow_tracker/stacked_borrows/diagnostics.rs index aa99a14b18..6d4a5bd41b 100644 --- a/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -20,7 +20,7 @@ fn err_sb_ub<'tcx>( #[derive(Clone, Debug)] pub struct AllocHistory { id: AllocId, - base: (Item, Span), + root: (Item, Span), creations: smallvec::SmallVec<[Creation; 1]>, invalidations: smallvec::SmallVec<[Invalidation; 1]>, protectors: smallvec::SmallVec<[Protection; 1]>, @@ -225,7 +225,7 @@ impl AllocHistory { pub fn new(id: AllocId, item: Item, machine: &MiriMachine<'_, '_>) -> Self { Self { id, - base: (item, machine.current_span()), + root: (item, machine.current_span()), creations: SmallVec::new(), invalidations: SmallVec::new(), protectors: SmallVec::new(), @@ -342,15 +342,15 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { }) }) .or_else(|| { - // If we didn't find a retag that created this tag, it might be the base tag of + // If we didn't find a retag that created this tag, it might be the root tag of // this allocation. - if self.history.base.0.tag() == tag { + if self.history.root.0.tag() == tag { Some(( format!( - "{tag:?} was created here, as the base tag for {:?}", + "{tag:?} was created here, as the root tag for {:?}", self.history.id ), - self.history.base.1.data(), + self.history.root.1.data(), )) } else { None diff --git a/src/borrow_tracker/stacked_borrows/mod.rs b/src/borrow_tracker/stacked_borrows/mod.rs index b4005515d9..a6dd1d829c 100644 --- a/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/borrow_tracker/stacked_borrows/mod.rs @@ -518,9 +518,9 @@ impl Stacks { // not through a pointer). That is, whenever we directly write to a local, this will pop // everything else off the stack, invalidating all previous pointers, // and in particular, *all* raw pointers. - MemoryKind::Stack => (state.base_ptr_tag(id, machine), Permission::Unique), + MemoryKind::Stack => (state.root_ptr_tag(id, machine), Permission::Unique), // Everything else is shared by default. - _ => (state.base_ptr_tag(id, machine), Permission::SharedReadWrite), + _ => (state.root_ptr_tag(id, machine), Permission::SharedReadWrite), }; Stacks::new(size, perm, base_tag, id, machine) } diff --git a/src/borrow_tracker/stacked_borrows/stack.rs b/src/borrow_tracker/stacked_borrows/stack.rs index 76430498e2..bebd14d2f1 100644 --- a/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/borrow_tracker/stacked_borrows/stack.rs @@ -47,7 +47,7 @@ impl Stack { let mut first_removed = None; // We never consider removing the bottom-most tag. For stacks without an unknown - // bottom this preserves the base tag. + // bottom this preserves the root tag. // Note that the algorithm below is based on considering the tag at read_idx - 1, // so precisely considering the tag at index 0 for removal when we have an unknown // bottom would complicate the implementation. The simplification of not considering @@ -93,7 +93,7 @@ impl Stack { self.unique_range = 0..self.len(); } - // Replace any Items which have been collected with the base item, a known-good value. + // Replace any Items which have been collected with the root item, a known-good value. for i in 0..CACHE_LEN { if self.cache.idx[i] >= first_removed { self.cache.items[i] = self.borrows[0]; @@ -331,7 +331,7 @@ impl<'tcx> Stack { self.verify_cache_consistency(); } - /// Construct a new `Stack` using the passed `Item` as the base tag. + /// Construct a new `Stack` using the passed `Item` as the root tag. pub fn new(item: Item) -> Self { Stack { borrows: vec![item], @@ -438,8 +438,8 @@ impl<'tcx> Stack { let mut removed = 0; let mut cursor = 0; // Remove invalid entries from the cache by rotating them to the end of the cache, then - // keep track of how many invalid elements there are and overwrite them with the base tag. - // The base tag here serves as a harmless default value. + // keep track of how many invalid elements there are and overwrite them with the root tag. + // The root tag here serves as a harmless default value. for _ in 0..CACHE_LEN - 1 { if self.cache.idx[cursor] >= start { self.cache.idx[cursor..CACHE_LEN - removed].rotate_left(1); diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index 492e324de4..fc5eb942a2 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -37,7 +37,7 @@ impl<'tcx> Tree { _kind: MemoryKind, machine: &MiriMachine<'_, 'tcx>, ) -> Self { - let tag = state.base_ptr_tag(id, machine); // Fresh tag for the root + let tag = state.root_ptr_tag(id, machine); // Fresh tag for the root let span = machine.current_span(); Tree::new(tag, size, span) } diff --git a/src/machine.rs b/src/machine.rs index 2bd1b24ee9..0bfc59e67d 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -503,7 +503,7 @@ pub struct MiriMachine<'mir, 'tcx> { /// Crates which are considered local for the purposes of error reporting. pub(crate) local_crates: Vec, - /// Mapping extern static names to their base pointer. + /// Mapping extern static names to their pointer. extern_statics: FxHashMap>, /// The random number generator used for resolving non-determinism. @@ -1042,14 +1042,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { ecx.generate_nan(inputs) } - fn thread_local_static_base_pointer( + fn thread_local_static_pointer( ecx: &mut MiriInterpCx<'mir, 'tcx>, def_id: DefId, ) -> InterpResult<'tcx, Pointer> { ecx.get_or_create_thread_local_alloc(def_id) } - fn extern_static_base_pointer( + fn extern_static_pointer( ecx: &MiriInterpCx<'mir, 'tcx>, def_id: DefId, ) -> InterpResult<'tcx, Pointer> { @@ -1135,7 +1135,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { weak_memory: buffer_alloc, backtrace, }, - |ptr| ecx.global_base_pointer(ptr), + |ptr| ecx.global_root_pointer(ptr), )?; if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) { @@ -1148,7 +1148,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { Ok(Cow::Owned(alloc)) } - fn adjust_alloc_base_pointer( + fn adjust_alloc_root_pointer( ecx: &MiriInterpCx<'mir, 'tcx>, ptr: Pointer, kind: Option, @@ -1159,22 +1159,22 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { // The machine promises to never call us on thread-local or extern statics. match ecx.tcx.try_get_global_alloc(alloc_id) { Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => { - panic!("adjust_alloc_base_pointer called on thread-local static") + panic!("adjust_alloc_root_pointer called on thread-local static") } Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => { - panic!("adjust_alloc_base_pointer called on extern static") + panic!("adjust_alloc_root_pointer called on extern static") } _ => {} } } // FIXME: can we somehow preserve the immutability of `ptr`? let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker { - borrow_tracker.borrow_mut().base_ptr_tag(alloc_id, &ecx.machine) + borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine) } else { // Value does not matter, SB is disabled BorTag::default() }; - ecx.adjust_alloc_base_pointer(ptr, tag, kind) + ecx.adjust_alloc_root_pointer(ptr, tag, kind) } /// Called on `usize as ptr` casts. diff --git a/tests/fail/both_borrows/invalidate_against_protector3.stack.stderr b/tests/fail/both_borrows/invalidate_against_protector3.stack.stderr index f6eeef33e9..8426f56004 100644 --- a/tests/fail/both_borrows/invalidate_against_protector3.stack.stderr +++ b/tests/fail/both_borrows/invalidate_against_protector3.stack.stderr @@ -6,7 +6,7 @@ LL | unsafe { *x = 0 }; | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information -help: was created here, as the base tag for ALLOC +help: was created here, as the root tag for ALLOC --> $DIR/invalidate_against_protector3.rs:LL:CC | LL | let ptr = alloc(Layout::for_value(&0i32)) as *mut i32; From 0e4c96953bf12ce8b9bedb8b62ad88187871ec36 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 17 Apr 2024 09:25:26 +0200 Subject: [PATCH 042/208] alloc_error_handler tests: directly call handle_alloc_error; test more codepaths --- tests/fail/alloc/alloc_error_handler.rs | 17 +----- tests/fail/alloc/alloc_error_handler.stderr | 4 +- .../fail/alloc/alloc_error_handler_custom.rs | 52 +++++++++++++++++++ .../alloc/alloc_error_handler_custom.stderr | 27 ++++++++++ .../fail/alloc/alloc_error_handler_no_std.rs | 34 +++--------- .../alloc/alloc_error_handler_no_std.stderr | 21 +++----- tests/panic/alloc_error_handler_hook.rs | 20 +++++++ tests/panic/alloc_error_handler_hook.stderr | 4 ++ tests/panic/alloc_error_handler_panic.rs | 18 +------ 9 files changed, 122 insertions(+), 75 deletions(-) create mode 100644 tests/fail/alloc/alloc_error_handler_custom.rs create mode 100644 tests/fail/alloc/alloc_error_handler_custom.stderr create mode 100644 tests/panic/alloc_error_handler_hook.rs create mode 100644 tests/panic/alloc_error_handler_hook.stderr diff --git a/tests/fail/alloc/alloc_error_handler.rs b/tests/fail/alloc/alloc_error_handler.rs index dc8e8c7380..2097126e16 100644 --- a/tests/fail/alloc/alloc_error_handler.rs +++ b/tests/fail/alloc/alloc_error_handler.rs @@ -4,22 +4,7 @@ #![feature(allocator_api)] use std::alloc::*; -use std::ptr::NonNull; - -struct BadAlloc; - -// Create a failing allocator; Miri's native allocator never fails so this is the only way to -// actually call the alloc error handler. -unsafe impl Allocator for BadAlloc { - fn allocate(&self, _l: Layout) -> Result, AllocError> { - Err(AllocError) - } - - unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { - unreachable!(); - } -} fn main() { - let _b = Box::new_in(0, BadAlloc); + handle_alloc_error(Layout::for_value(&0)); } diff --git a/tests/fail/alloc/alloc_error_handler.stderr b/tests/fail/alloc/alloc_error_handler.stderr index f9d8e80c0f..d1731a0f42 100644 --- a/tests/fail/alloc/alloc_error_handler.stderr +++ b/tests/fail/alloc/alloc_error_handler.stderr @@ -12,12 +12,10 @@ LL | ABORT(); = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC - = note: inside `std::boxed::Box::::new_uninit_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC - = note: inside `std::boxed::Box::::new_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC note: inside `main` --> $DIR/alloc_error_handler.rs:LL:CC | -LL | let _b = Box::new_in(0, BadAlloc); +LL | handle_alloc_error(Layout::for_value(&0)); | ^ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace diff --git a/tests/fail/alloc/alloc_error_handler_custom.rs b/tests/fail/alloc/alloc_error_handler_custom.rs new file mode 100644 index 0000000000..9f8837a626 --- /dev/null +++ b/tests/fail/alloc/alloc_error_handler_custom.rs @@ -0,0 +1,52 @@ +//@compile-flags: -Cpanic=abort +#![feature(start, core_intrinsics)] +#![feature(alloc_error_handler)] +#![feature(allocator_api)] +#![no_std] + +extern crate alloc; + +use alloc::alloc::*; + +extern "Rust" { + fn miri_write_to_stderr(bytes: &[u8]); +} + +#[alloc_error_handler] +fn alloc_error_handler(_: Layout) -> ! { + let msg = "custom alloc error handler called!\n"; + unsafe { miri_write_to_stderr(msg.as_bytes()) }; + core::intrinsics::abort(); //~ERROR: aborted +} + +// rustc requires us to provide some more things that aren't actually used by this test +mod plumbing { + use super::*; + + #[panic_handler] + fn panic_handler(_: &core::panic::PanicInfo) -> ! { + let msg = "custom panic handler called!\n"; + unsafe { miri_write_to_stderr(msg.as_bytes()) }; + core::intrinsics::abort(); + } + + struct NoAlloc; + + unsafe impl GlobalAlloc for NoAlloc { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { + unreachable!(); + } + + unsafe fn dealloc(&self, _: *mut u8, _: Layout) { + unreachable!(); + } + } + + #[global_allocator] + static GLOBAL: NoAlloc = NoAlloc; +} + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + handle_alloc_error(Layout::for_value(&0)); +} diff --git a/tests/fail/alloc/alloc_error_handler_custom.stderr b/tests/fail/alloc/alloc_error_handler_custom.stderr new file mode 100644 index 0000000000..cdd666e5e0 --- /dev/null +++ b/tests/fail/alloc/alloc_error_handler_custom.stderr @@ -0,0 +1,27 @@ +custom alloc error handler called! +error: abnormal termination: the program aborted execution + --> $DIR/alloc_error_handler_custom.rs:LL:CC + | +LL | core::intrinsics::abort(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution + | + = note: BACKTRACE: + = note: inside `alloc_error_handler` at $DIR/alloc_error_handler_custom.rs:LL:CC +note: inside `_::__rg_oom` + --> $DIR/alloc_error_handler_custom.rs:LL:CC + | +LL | #[alloc_error_handler] + | ---------------------- in this procedural macro expansion +LL | fn alloc_error_handler(_: Layout) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC + = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC +note: inside `start` + --> $DIR/alloc_error_handler_custom.rs:LL:CC + | +LL | handle_alloc_error(Layout::for_value(&0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/fail/alloc/alloc_error_handler_no_std.rs b/tests/fail/alloc/alloc_error_handler_no_std.rs index 8103296f47..e3949b889f 100644 --- a/tests/fail/alloc/alloc_error_handler_no_std.rs +++ b/tests/fail/alloc/alloc_error_handler_no_std.rs @@ -7,28 +7,16 @@ extern crate alloc; use alloc::alloc::*; -use alloc::boxed::Box; -use core::ptr::NonNull; -struct BadAlloc; - -// Create a failing allocator; that is the only way to actually call the alloc error handler. -unsafe impl Allocator for BadAlloc { - fn allocate(&self, _l: Layout) -> Result, AllocError> { - Err(AllocError) - } - - unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { - unreachable!(); - } +extern "Rust" { + fn miri_write_to_stderr(bytes: &[u8]); } -#[alloc_error_handler] -fn alloc_error_handler(_: Layout) -> ! { - extern "Rust" { - fn miri_write_to_stderr(bytes: &[u8]); - } - let msg = "custom alloc error handler called!\n"; +// The default no_std alloc_error_handler is a panic. + +#[panic_handler] +fn panic_handler(_panic_info: &core::panic::PanicInfo) -> ! { + let msg = "custom panic handler called!\n"; unsafe { miri_write_to_stderr(msg.as_bytes()) }; core::intrinsics::abort(); //~ERROR: aborted } @@ -37,11 +25,6 @@ fn alloc_error_handler(_: Layout) -> ! { mod plumbing { use super::*; - #[panic_handler] - fn panic_handler(_: &core::panic::PanicInfo) -> ! { - core::intrinsics::abort(); - } - struct NoAlloc; unsafe impl GlobalAlloc for NoAlloc { @@ -60,6 +43,5 @@ mod plumbing { #[start] fn start(_: isize, _: *const *const u8) -> isize { - let _b = Box::new_in(0, BadAlloc); - 0 + handle_alloc_error(Layout::for_value(&0)); } diff --git a/tests/fail/alloc/alloc_error_handler_no_std.stderr b/tests/fail/alloc/alloc_error_handler_no_std.stderr index b40ffb7012..906c51be8b 100644 --- a/tests/fail/alloc/alloc_error_handler_no_std.stderr +++ b/tests/fail/alloc/alloc_error_handler_no_std.stderr @@ -1,4 +1,4 @@ -custom alloc error handler called! +custom panic handler called! error: abnormal termination: the program aborted execution --> $DIR/alloc_error_handler_no_std.rs:LL:CC | @@ -6,24 +6,17 @@ LL | core::intrinsics::abort(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution | = note: BACKTRACE: - = note: inside `alloc_error_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC -note: inside `_::__rg_oom` - --> $DIR/alloc_error_handler_no_std.rs:LL:CC - | -LL | #[alloc_error_handler] - | ---------------------- in this procedural macro expansion -LL | fn alloc_error_handler(_: Layout) -> ! { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: inside `panic_handler` at $DIR/alloc_error_handler_no_std.rs:LL:CC + = note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC - = note: inside `alloc::boxed::Box::::new_uninit_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC - = note: inside `alloc::boxed::Box::::new_in` at RUSTLIB/alloc/src/boxed.rs:LL:CC note: inside `start` --> $DIR/alloc_error_handler_no_std.rs:LL:CC | -LL | let _b = Box::new_in(0, BadAlloc); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info) +LL | handle_alloc_error(Layout::for_value(&0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace error: aborting due to 1 previous error diff --git a/tests/panic/alloc_error_handler_hook.rs b/tests/panic/alloc_error_handler_hook.rs new file mode 100644 index 0000000000..a1eadb45fd --- /dev/null +++ b/tests/panic/alloc_error_handler_hook.rs @@ -0,0 +1,20 @@ +#![feature(allocator_api, alloc_error_hook)] + +use std::alloc::*; + +struct Bomb; +impl Drop for Bomb { + fn drop(&mut self) { + eprintln!("yes we are unwinding!"); + } +} + +#[allow(unreachable_code, unused_variables)] +fn main() { + // This is a particularly tricky hook, since it unwinds, which the default one does not. + set_alloc_error_hook(|_layout| panic!("alloc error hook called")); + + let bomb = Bomb; + handle_alloc_error(Layout::for_value(&0)); + std::mem::forget(bomb); // defuse unwinding bomb +} diff --git a/tests/panic/alloc_error_handler_hook.stderr b/tests/panic/alloc_error_handler_hook.stderr new file mode 100644 index 0000000000..01c286fe3d --- /dev/null +++ b/tests/panic/alloc_error_handler_hook.stderr @@ -0,0 +1,4 @@ +thread 'main' panicked at $DIR/alloc_error_handler_hook.rs:LL:CC: +alloc error hook called +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +yes we are unwinding! diff --git a/tests/panic/alloc_error_handler_panic.rs b/tests/panic/alloc_error_handler_panic.rs index 186c9667a9..c434e8d322 100644 --- a/tests/panic/alloc_error_handler_panic.rs +++ b/tests/panic/alloc_error_handler_panic.rs @@ -2,21 +2,6 @@ #![feature(allocator_api)] use std::alloc::*; -use std::ptr::NonNull; - -struct BadAlloc; - -// Create a failing allocator; Miri's native allocator never fails so this is the only way to -// actually call the alloc error handler. -unsafe impl Allocator for BadAlloc { - fn allocate(&self, _l: Layout) -> Result, AllocError> { - Err(AllocError) - } - - unsafe fn deallocate(&self, _ptr: NonNull, _layout: Layout) { - unreachable!(); - } -} struct Bomb; impl Drop for Bomb { @@ -25,8 +10,9 @@ impl Drop for Bomb { } } +#[allow(unreachable_code, unused_variables)] fn main() { let bomb = Bomb; - let _b = Box::new_in(0, BadAlloc); + handle_alloc_error(Layout::for_value(&0)); std::mem::forget(bomb); // defuse unwinding bomb } From 5aad4fb1ab1ddb19dcc1e15767c4f136f831c13f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 17 Apr 2024 09:40:53 +0200 Subject: [PATCH 043/208] tests/utils: add fmt::Write implementations for miri's native stdout/stderr --- .../fail/alloc/alloc_error_handler_custom.rs | 15 +++++------ .../alloc/alloc_error_handler_custom.stderr | 6 ++--- .../fail/alloc/alloc_error_handler_no_std.rs | 12 ++++----- .../alloc/alloc_error_handler_no_std.stderr | 2 ++ tests/fail/panic/no_std.rs | 24 +++--------------- tests/pass/no_std.rs | 22 +++------------- tests/utils/io.rs | 25 +++++++++++++++++++ tests/utils/miri_extern.rs | 4 +-- tests/utils/mod.no_std.rs | 11 ++++++++ tests/utils/mod.rs | 2 ++ 10 files changed, 64 insertions(+), 59 deletions(-) create mode 100644 tests/utils/io.rs create mode 100644 tests/utils/mod.no_std.rs diff --git a/tests/fail/alloc/alloc_error_handler_custom.rs b/tests/fail/alloc/alloc_error_handler_custom.rs index 9f8837a626..babdb73f09 100644 --- a/tests/fail/alloc/alloc_error_handler_custom.rs +++ b/tests/fail/alloc/alloc_error_handler_custom.rs @@ -7,15 +7,14 @@ extern crate alloc; use alloc::alloc::*; +use core::fmt::Write; -extern "Rust" { - fn miri_write_to_stderr(bytes: &[u8]); -} +#[path = "../../utils/mod.no_std.rs"] +mod utils; #[alloc_error_handler] -fn alloc_error_handler(_: Layout) -> ! { - let msg = "custom alloc error handler called!\n"; - unsafe { miri_write_to_stderr(msg.as_bytes()) }; +fn alloc_error_handler(layout: Layout) -> ! { + let _ = writeln!(utils::MiriStderr, "custom alloc error handler: {layout:?}"); core::intrinsics::abort(); //~ERROR: aborted } @@ -25,9 +24,7 @@ mod plumbing { #[panic_handler] fn panic_handler(_: &core::panic::PanicInfo) -> ! { - let msg = "custom panic handler called!\n"; - unsafe { miri_write_to_stderr(msg.as_bytes()) }; - core::intrinsics::abort(); + loop {} } struct NoAlloc; diff --git a/tests/fail/alloc/alloc_error_handler_custom.stderr b/tests/fail/alloc/alloc_error_handler_custom.stderr index cdd666e5e0..5d9c2e2fb4 100644 --- a/tests/fail/alloc/alloc_error_handler_custom.stderr +++ b/tests/fail/alloc/alloc_error_handler_custom.stderr @@ -1,4 +1,4 @@ -custom alloc error handler called! +custom alloc error handler: Layout { size: 4, align: 4 (1 << 2) } error: abnormal termination: the program aborted execution --> $DIR/alloc_error_handler_custom.rs:LL:CC | @@ -12,8 +12,8 @@ note: inside `_::__rg_oom` | LL | #[alloc_error_handler] | ---------------------- in this procedural macro expansion -LL | fn alloc_error_handler(_: Layout) -> ! { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn alloc_error_handler(layout: Layout) -> ! { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC = note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC note: inside `start` diff --git a/tests/fail/alloc/alloc_error_handler_no_std.rs b/tests/fail/alloc/alloc_error_handler_no_std.rs index e3949b889f..18a8a61f22 100644 --- a/tests/fail/alloc/alloc_error_handler_no_std.rs +++ b/tests/fail/alloc/alloc_error_handler_no_std.rs @@ -7,17 +7,17 @@ extern crate alloc; use alloc::alloc::*; +use core::fmt::Write; -extern "Rust" { - fn miri_write_to_stderr(bytes: &[u8]); -} +#[path = "../../utils/mod.no_std.rs"] +mod utils; // The default no_std alloc_error_handler is a panic. #[panic_handler] -fn panic_handler(_panic_info: &core::panic::PanicInfo) -> ! { - let msg = "custom panic handler called!\n"; - unsafe { miri_write_to_stderr(msg.as_bytes()) }; +fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { + let _ = writeln!(utils::MiriStderr, "custom panic handler called!"); + let _ = writeln!(utils::MiriStderr, "{panic_info}"); core::intrinsics::abort(); //~ERROR: aborted } diff --git a/tests/fail/alloc/alloc_error_handler_no_std.stderr b/tests/fail/alloc/alloc_error_handler_no_std.stderr index 906c51be8b..6b98f6f75d 100644 --- a/tests/fail/alloc/alloc_error_handler_no_std.stderr +++ b/tests/fail/alloc/alloc_error_handler_no_std.stderr @@ -1,4 +1,6 @@ custom panic handler called! +panicked at RUSTLIB/alloc/src/alloc.rs:LL:CC: +memory allocation of 4 bytes failed error: abnormal termination: the program aborted execution --> $DIR/alloc_error_handler_no_std.rs:LL:CC | diff --git a/tests/fail/panic/no_std.rs b/tests/fail/panic/no_std.rs index 26cc0b2782..4d32b6d746 100644 --- a/tests/fail/panic/no_std.rs +++ b/tests/fail/panic/no_std.rs @@ -1,27 +1,11 @@ +//@compile-flags: -Cpanic=abort #![feature(start, core_intrinsics)] #![no_std] -//@compile-flags: -Cpanic=abort - -// Plumbing to let us use `writeln!` to host stderr: - -extern "Rust" { - fn miri_write_to_stderr(bytes: &[u8]); -} - -struct HostErr; use core::fmt::Write; -impl Write for HostErr { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - unsafe { - miri_write_to_stderr(s.as_bytes()); - } - Ok(()) - } -} - -// Aaaand the test: +#[path = "../../utils/mod.no_std.rs"] +mod utils; #[start] fn start(_: isize, _: *const *const u8) -> isize { @@ -30,6 +14,6 @@ fn start(_: isize, _: *const *const u8) -> isize { #[panic_handler] fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { - writeln!(HostErr, "{panic_info}").ok(); + writeln!(utils::MiriStderr, "{panic_info}").ok(); core::intrinsics::abort(); //~ ERROR: the program aborted execution } diff --git a/tests/pass/no_std.rs b/tests/pass/no_std.rs index 3c98ee50aa..fc1c16f5fb 100644 --- a/tests/pass/no_std.rs +++ b/tests/pass/no_std.rs @@ -2,30 +2,14 @@ #![feature(start)] #![no_std] -// Plumbing to let us use `writeln!` to host stdout: - -extern "Rust" { - fn miri_write_to_stdout(bytes: &[u8]); -} - -struct Host; - use core::fmt::Write; -impl Write for Host { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - unsafe { - miri_write_to_stdout(s.as_bytes()); - } - Ok(()) - } -} - -// Aaaand the test: +#[path = "../utils/mod.no_std.rs"] +mod utils; #[start] fn start(_: isize, _: *const *const u8) -> isize { - writeln!(Host, "hello, world!").unwrap(); + writeln!(utils::MiriStdout, "hello, world!").unwrap(); 0 } diff --git a/tests/utils/io.rs b/tests/utils/io.rs new file mode 100644 index 0000000000..e3eaa6c468 --- /dev/null +++ b/tests/utils/io.rs @@ -0,0 +1,25 @@ +use core::fmt::{self, Write}; + +use super::miri_extern; + +pub struct MiriStderr; + +impl Write for MiriStderr { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + miri_extern::miri_write_to_stderr(s.as_bytes()); + } + Ok(()) + } +} + +pub struct MiriStdout; + +impl Write for MiriStdout { + fn write_str(&mut self, s: &str) -> fmt::Result { + unsafe { + miri_extern::miri_write_to_stdout(s.as_bytes()); + } + Ok(()) + } +} diff --git a/tests/utils/miri_extern.rs b/tests/utils/miri_extern.rs index e2983f6c71..d6c43b1882 100644 --- a/tests/utils/miri_extern.rs +++ b/tests/utils/miri_extern.rs @@ -133,8 +133,8 @@ extern "Rust" { /// with a null terminator. /// Returns 0 if the `out` buffer was large enough, and the required size otherwise. pub fn miri_host_to_target_path( - path: *const std::ffi::c_char, - out: *mut std::ffi::c_char, + path: *const core::ffi::c_char, + out: *mut core::ffi::c_char, out_size: usize, ) -> usize; diff --git a/tests/utils/mod.no_std.rs b/tests/utils/mod.no_std.rs new file mode 100644 index 0000000000..aaf2bf50c4 --- /dev/null +++ b/tests/utils/mod.no_std.rs @@ -0,0 +1,11 @@ +#![allow(dead_code)] +#![allow(unused_imports)] + +#[macro_use] +mod macros; + +mod io; +mod miri_extern; + +pub use self::io::*; +pub use self::miri_extern::*; diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index cb9380f575..138ada4e20 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -5,9 +5,11 @@ mod macros; mod fs; +mod io; mod miri_extern; pub use self::fs::*; +pub use self::io::*; pub use self::miri_extern::*; pub fn run_provenance_gc() { From 1f1f613ba49a6ed8b69d222db4270358080b2fc1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 17 Apr 2024 09:44:28 +0200 Subject: [PATCH 044/208] no need to use miri's native stderr here --- tests/pass/tree_borrows/reserved.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/pass/tree_borrows/reserved.rs b/tests/pass/tree_borrows/reserved.rs index 87ce91a809..f93cac8361 100644 --- a/tests/pass/tree_borrows/reserved.rs +++ b/tests/pass/tree_borrows/reserved.rs @@ -27,9 +27,8 @@ fn main() { } } -unsafe fn print(msg: &str) { - utils::miri_write_to_stderr(msg.as_bytes()); - utils::miri_write_to_stderr("\n".as_bytes()); +fn print(msg: &str) { + eprintln!("{msg}"); } unsafe fn read_second(x: &mut T, y: *mut u8) { From 951aa0d88ad049ee397f7464f0279fbb0dd4542e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 17 Apr 2024 20:38:15 +0200 Subject: [PATCH 045/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index bd87405da3..f8f3016b15 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -803e33a4460c82581bd01d4008d0f44aef1ddfe8 +c45dee5efd0c042e9d1e24559ebd0d6424d8aa70 From 1f6e83141f9fc556829b214f7206416c22cbd79b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 17 Apr 2024 21:20:25 +0200 Subject: [PATCH 046/208] fmt --- src/alloc_addresses/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index b4983656ad..3e00e6037c 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -141,11 +141,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn addr_from_alloc_id( - &self, - alloc_id: AllocId, - _kind: MemoryKind, - ) -> InterpResult<'tcx, u64> { + fn addr_from_alloc_id(&self, alloc_id: AllocId, _kind: MemoryKind) -> InterpResult<'tcx, u64> { let ecx = self.eval_context_ref(); let mut global_state = ecx.machine.alloc_addresses.borrow_mut(); let global_state = &mut *global_state; From 1302a4b498c2d5eac3c6194c656fb0adcb2143c5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 16 Apr 2024 17:02:20 +1000 Subject: [PATCH 047/208] Simplify `static_assert_size`s. We want to run them on all 64-bit platforms. --- src/machine.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 1d06d5c69d..e3021fc7e2 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -241,12 +241,12 @@ pub enum ProvenanceExtra { Wildcard, } -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] static_assert_size!(Pointer, 24); // FIXME: this would with in 24bytes but layout optimizations are not smart enough -// #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +// #[cfg(target_pointer_width = "64")] //static_assert_size!(Pointer>, 24); -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[cfg(target_pointer_width = "64")] static_assert_size!(Scalar, 32); impl fmt::Debug for Provenance { From 59fdf8ac7228478089ac6e8df2208adc5a61165c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 10:23:22 +0200 Subject: [PATCH 048/208] add test for Drop terminator on non-drop type --- tests/pass/drop_type_without_drop_glue.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/pass/drop_type_without_drop_glue.rs diff --git a/tests/pass/drop_type_without_drop_glue.rs b/tests/pass/drop_type_without_drop_glue.rs new file mode 100644 index 0000000000..43ddc8a4d8 --- /dev/null +++ b/tests/pass/drop_type_without_drop_glue.rs @@ -0,0 +1,21 @@ +#![feature(custom_mir, core_intrinsics, strict_provenance)] +use std::intrinsics::mir::*; + +// The `Drop` terminator on a type with no drop glue should be a NOP. + +#[custom_mir(dialect = "runtime", phase = "optimized")] +fn drop_in_place_with_terminator(ptr: *mut i32) { + mir! { + { + Drop(*ptr, ReturnTo(after_call), UnwindContinue()) + } + after_call = { + Return() + } + } +} + +pub fn main() { + drop_in_place_with_terminator(std::ptr::without_provenance_mut(0)); + drop_in_place_with_terminator(std::ptr::without_provenance_mut(1)); +} From 65a7d204179992a7bbedcb09ab69f2af86131495 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 10:58:49 +0200 Subject: [PATCH 049/208] move read_byte_slice to general helpers file, next to read_c_str --- src/helpers.rs | 17 ++++++++++++++++- src/shims/foreign_items.rs | 20 +++++--------------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index e2c6769ccb..784568ec56 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -912,10 +912,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }) } + /// Read bytes from a byte slice. + fn read_byte_slice<'a>( + &'a self, + slice: &ImmTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, &'a [u8]> + where + 'mir: 'a, + { + let this = self.eval_context_ref(); + let (ptr, len) = slice.to_scalar_pair(); + let ptr = ptr.to_pointer(this)?; + let len = len.to_target_usize(this)?; + let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; + Ok(bytes) + } + /// Read a sequence of bytes until the first null terminator. fn read_c_str<'a>(&'a self, ptr: Pointer>) -> InterpResult<'tcx, &'a [u8]> where - 'tcx: 'a, 'mir: 'a, { let this = self.eval_context_ref(); diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index e6fc29a5ae..acbc36b05b 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -305,19 +305,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Read bytes from a `(ptr, len)` argument - fn read_byte_slice<'i>(&'i self, bytes: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, &'i [u8]> - where - 'mir: 'i, - { - let this = self.eval_context_ref(); - let (ptr, len) = this.read_immediate(bytes)?.to_scalar_pair(); - let ptr = ptr.to_pointer(this)?; - let len = len.to_target_usize(this)?; - let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?; - Ok(bytes) - } - /// Returns the minimum alignment for the target architecture for allocations of the given size. fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { let this = self.eval_context_ref(); @@ -466,7 +453,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [ptr, nth_parent, name] = this.check_shim(abi, Abi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nth_parent = this.read_scalar(nth_parent)?.to_u8()?; - let name = this.read_byte_slice(name)?; + let name = this.read_immediate(name)?; + + let name = this.read_byte_slice(&name)?; // We must make `name` owned because we need to // end the shared borrow from `read_byte_slice` before we can // start the mutable borrow for `give_pointer_debug_name`. @@ -527,7 +516,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // README for details. "miri_write_to_stdout" | "miri_write_to_stderr" => { let [msg] = this.check_shim(abi, Abi::Rust, link_name, args)?; - let msg = this.read_byte_slice(msg)?; + let msg = this.read_immediate(msg)?; + let msg = this.read_byte_slice(&msg)?; // Note: we're ignoring errors writing to host stdout/stderr. let _ignore = match link_name.as_str() { "miri_write_to_stdout" => std::io::stdout().write_all(msg), From 74a9f06074cea0772cfc699ddf6474759c5ff5e8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 11:12:58 +0200 Subject: [PATCH 050/208] add test checking that we do run MIR validation --- tests/panic/mir-validation.rs | 21 +++++++++++++++++++++ tests/panic/mir-validation.stderr | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 tests/panic/mir-validation.rs create mode 100644 tests/panic/mir-validation.stderr diff --git a/tests/panic/mir-validation.rs b/tests/panic/mir-validation.rs new file mode 100644 index 0000000000..5e207c2609 --- /dev/null +++ b/tests/panic/mir-validation.rs @@ -0,0 +1,21 @@ +//! Ensure that the MIR validator runs on Miri's input. +//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@normalize-stderr-test: "\n +at [^\n]+" -> "" +//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" +//@normalize-stderr-test: "\n[ =]*note:.*" -> "" +#![feature(custom_mir, core_intrinsics)] +use core::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime", phase = "optimized")] +pub fn main() { + mir! { + let x: i32; + let tuple: (*mut i32,); + { + tuple.0 = core::ptr::addr_of_mut!(x); + // Deref at the wrong place! + *(tuple.0) = 1; + Return() + } + } +} diff --git a/tests/panic/mir-validation.stderr b/tests/panic/mir-validation.stderr new file mode 100644 index 0000000000..243fed020e --- /dev/null +++ b/tests/panic/mir-validation.stderr @@ -0,0 +1,21 @@ +thread 'rustc' panicked at compiler/rustc_const_eval/src/transform/validate.rs:LL:CC: +broken MIR in Item(DefId(0:4 ~ mir_validation[fad2]::main)) (after phase change to runtime-optimized) at bb0[1]: +(*(_2.0: *mut i32)), has deref at the wrong place +stack backtrace: + +error: the compiler unexpectedly panicked. this is a bug. + + + + +query stack during panic: +#0 [optimized_mir] optimizing MIR for `main` +end of query stack + +Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic: + --> RUSTLIB/core/src/ops/function.rs:LL:CC + | +LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + From 0a4a2364f05d46682e24178e75270dc24f7ccf93 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 11:14:37 +0200 Subject: [PATCH 051/208] when suggesting RUST_BACKTRACE=1, add a special note for Miri's env var isolation --- tests/fail/function_calls/exported_symbol_bad_unwind1.stderr | 1 + .../fail/function_calls/exported_symbol_bad_unwind2.both.stderr | 1 + .../function_calls/exported_symbol_bad_unwind2.definition.stderr | 1 + .../exported_symbol_bad_unwind2.extern_block.stderr | 1 + tests/fail/function_calls/return_pointer_on_unwind.stderr | 1 + tests/fail/intrinsics/uninit_uninhabited_type.stderr | 1 + tests/fail/intrinsics/zero_fn_ptr.stderr | 1 + tests/fail/panic/bad_unwind.stderr | 1 + tests/fail/panic/double_panic.stderr | 1 + tests/fail/panic/panic_abort1.stderr | 1 + tests/fail/panic/panic_abort2.stderr | 1 + tests/fail/panic/panic_abort3.stderr | 1 + tests/fail/panic/panic_abort4.stderr | 1 + tests/fail/terminate-terminator.stderr | 1 + tests/fail/unwind-action-terminate.stderr | 1 + tests/panic/div-by-zero-2.stderr | 1 + tests/panic/function_calls/exported_symbol_good_unwind.stderr | 1 + tests/panic/oob_subslice.stderr | 1 + tests/panic/overflowing-lsh-neg.stderr | 1 + tests/panic/overflowing-rsh-1.stderr | 1 + tests/panic/overflowing-rsh-2.stderr | 1 + tests/panic/panic2.stderr | 1 + tests/panic/panic3.stderr | 1 + tests/panic/panic4.stderr | 1 + tests/panic/transmute_fat2.stderr | 1 + tests/panic/unsupported_foreign_function.stderr | 1 + tests/panic/unsupported_syscall.stderr | 1 + tests/pass/panic/catch_panic.stderr | 1 + tests/pass/panic/concurrent-panic.stderr | 1 + tests/pass/panic/nested_panic_caught.stderr | 1 + tests/pass/panic/thread_panic.stderr | 1 + 31 files changed, 31 insertions(+) diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr b/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr index ab32883a03..42beed4ecd 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr +++ b/tests/fail/function_calls/exported_symbol_bad_unwind1.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/exported_symbol_bad_unwind1.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding --> $DIR/exported_symbol_bad_unwind1.rs:LL:CC | diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr index 9774e1e1a7..d88781ed22 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr +++ b/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: panic in a function that cannot unwind stack backtrace: diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr index 9774e1e1a7..d88781ed22 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr +++ b/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: panic in a function that cannot unwind stack backtrace: diff --git a/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr b/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr index f89cee0b86..bc3e485871 100644 --- a/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr +++ b/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/exported_symbol_bad_unwind2.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding --> $DIR/exported_symbol_bad_unwind2.rs:LL:CC | diff --git a/tests/fail/function_calls/return_pointer_on_unwind.stderr b/tests/fail/function_calls/return_pointer_on_unwind.stderr index 7f035eb5e8..a2fa4c1d59 100644 --- a/tests/fail/function_calls/return_pointer_on_unwind.stderr +++ b/tests/fail/function_calls/return_pointer_on_unwind.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/return_pointer_on_unwind.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> $DIR/return_pointer_on_unwind.rs:LL:CC | diff --git a/tests/fail/intrinsics/uninit_uninhabited_type.stderr b/tests/fail/intrinsics/uninit_uninhabited_type.stderr index 4723eddaa6..447f7cae6c 100644 --- a/tests/fail/intrinsics/uninit_uninhabited_type.stderr +++ b/tests/fail/intrinsics/uninit_uninhabited_type.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: aborted execution: attempted to instantiate uninhabited type `!` note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC diff --git a/tests/fail/intrinsics/zero_fn_ptr.stderr b/tests/fail/intrinsics/zero_fn_ptr.stderr index 9c6dd10079..bae3414980 100644 --- a/tests/fail/intrinsics/zero_fn_ptr.stderr +++ b/tests/fail/intrinsics/zero_fn_ptr.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: aborted execution: attempted to zero-initialize type `fn()`, which is invalid note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread caused non-unwinding panic. aborting. error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC diff --git a/tests/fail/panic/bad_unwind.stderr b/tests/fail/panic/bad_unwind.stderr index e4af01feb8..230e8337c7 100644 --- a/tests/fail/panic/bad_unwind.stderr +++ b/tests/fail/panic/bad_unwind.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/bad_unwind.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: Undefined Behavior: unwinding past a stack frame that does not allow unwinding --> $DIR/bad_unwind.rs:LL:CC | diff --git a/tests/fail/panic/double_panic.stderr b/tests/fail/panic/double_panic.stderr index e3cacbd27b..3b7a1511fa 100644 --- a/tests/fail/panic/double_panic.stderr +++ b/tests/fail/panic/double_panic.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/double_panic.rs:LL:CC: first note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at $DIR/double_panic.rs:LL:CC: second stack backtrace: diff --git a/tests/fail/panic/panic_abort1.stderr b/tests/fail/panic/panic_abort1.stderr index 6045569361..7694cc70b2 100644 --- a/tests/fail/panic/panic_abort1.stderr +++ b/tests/fail/panic/panic_abort1.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/panic_abort1.rs:LL:CC: panicking from libstd note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution --> RUSTLIB/panic_abort/src/lib.rs:LL:CC | diff --git a/tests/fail/panic/panic_abort2.stderr b/tests/fail/panic/panic_abort2.stderr index 7bb27e4baa..e6a4380ea5 100644 --- a/tests/fail/panic/panic_abort2.stderr +++ b/tests/fail/panic/panic_abort2.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/panic_abort2.rs:LL:CC: 42-panicking from libstd note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution --> RUSTLIB/panic_abort/src/lib.rs:LL:CC | diff --git a/tests/fail/panic/panic_abort3.stderr b/tests/fail/panic/panic_abort3.stderr index b46e8c2079..23e2021eee 100644 --- a/tests/fail/panic/panic_abort3.stderr +++ b/tests/fail/panic/panic_abort3.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/panic_abort3.rs:LL:CC: panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution --> RUSTLIB/panic_abort/src/lib.rs:LL:CC | diff --git a/tests/fail/panic/panic_abort4.stderr b/tests/fail/panic/panic_abort4.stderr index b15f720e43..20a0ddb901 100644 --- a/tests/fail/panic/panic_abort4.stderr +++ b/tests/fail/panic/panic_abort4.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/panic_abort4.rs:LL:CC: 42-panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution --> RUSTLIB/panic_abort/src/lib.rs:LL:CC | diff --git a/tests/fail/terminate-terminator.stderr b/tests/fail/terminate-terminator.stderr index 8dbc802bf5..f737adc561 100644 --- a/tests/fail/terminate-terminator.stderr +++ b/tests/fail/terminate-terminator.stderr @@ -3,6 +3,7 @@ warning: You have explicitly enabled MIR optimizations, overriding Miri's defaul thread 'main' panicked at $DIR/terminate-terminator.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: panic in a function that cannot unwind stack backtrace: diff --git a/tests/fail/unwind-action-terminate.stderr b/tests/fail/unwind-action-terminate.stderr index 1323a39710..7e722f7be3 100644 --- a/tests/fail/unwind-action-terminate.stderr +++ b/tests/fail/unwind-action-terminate.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/unwind-action-terminate.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at RUSTLIB/core/src/panicking.rs:LL:CC: panic in a function that cannot unwind stack backtrace: diff --git a/tests/panic/div-by-zero-2.stderr b/tests/panic/div-by-zero-2.stderr index 0f088ddd1a..f0b84ea6fd 100644 --- a/tests/panic/div-by-zero-2.stderr +++ b/tests/panic/div-by-zero-2.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/div-by-zero-2.rs:LL:CC: attempt to divide by zero note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/function_calls/exported_symbol_good_unwind.stderr b/tests/panic/function_calls/exported_symbol_good_unwind.stderr index acc0e58885..f77f6f0111 100644 --- a/tests/panic/function_calls/exported_symbol_good_unwind.stderr +++ b/tests/panic/function_calls/exported_symbol_good_unwind.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC: explicit panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC: explicit panic thread 'main' panicked at $DIR/exported_symbol_good_unwind.rs:LL:CC: diff --git a/tests/panic/oob_subslice.stderr b/tests/panic/oob_subslice.stderr index 2bccb60352..c116f8eb52 100644 --- a/tests/panic/oob_subslice.stderr +++ b/tests/panic/oob_subslice.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/oob_subslice.rs:LL:CC: range end index 5 out of range for slice of length 4 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/overflowing-lsh-neg.stderr b/tests/panic/overflowing-lsh-neg.stderr index 2cff81c58a..1d057ea5eb 100644 --- a/tests/panic/overflowing-lsh-neg.stderr +++ b/tests/panic/overflowing-lsh-neg.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/overflowing-lsh-neg.rs:LL:CC: attempt to shift left with overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/overflowing-rsh-1.stderr b/tests/panic/overflowing-rsh-1.stderr index 13117df5fc..d1a79400bf 100644 --- a/tests/panic/overflowing-rsh-1.stderr +++ b/tests/panic/overflowing-rsh-1.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/overflowing-rsh-1.rs:LL:CC: attempt to shift right with overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/overflowing-rsh-2.stderr b/tests/panic/overflowing-rsh-2.stderr index 986a66f899..612b0c0c4c 100644 --- a/tests/panic/overflowing-rsh-2.stderr +++ b/tests/panic/overflowing-rsh-2.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/overflowing-rsh-2.rs:LL:CC: attempt to shift right with overflow note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/panic2.stderr b/tests/panic/panic2.stderr index bee1af2bef..792c71346f 100644 --- a/tests/panic/panic2.stderr +++ b/tests/panic/panic2.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/panic2.rs:LL:CC: 42-panicking from libstd note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/panic3.stderr b/tests/panic/panic3.stderr index 8dac000d29..f8016bc391 100644 --- a/tests/panic/panic3.stderr +++ b/tests/panic/panic3.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/panic3.rs:LL:CC: panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/panic4.stderr b/tests/panic/panic4.stderr index 13437fe7f0..67410bf3b1 100644 --- a/tests/panic/panic4.stderr +++ b/tests/panic/panic4.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/panic4.rs:LL:CC: 42-panicking from libcore note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/transmute_fat2.stderr b/tests/panic/transmute_fat2.stderr index 1f09a2c1a0..2ee01d4693 100644 --- a/tests/panic/transmute_fat2.stderr +++ b/tests/panic/transmute_fat2.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/transmute_fat2.rs:LL:CC: index out of bounds: the len is 0 but the index is 0 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/unsupported_foreign_function.stderr b/tests/panic/unsupported_foreign_function.stderr index 4db1e29096..d0a7d8dafc 100644 --- a/tests/panic/unsupported_foreign_function.stderr +++ b/tests/panic/unsupported_foreign_function.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/unsupported_foreign_function.rs:LL:CC: unsupported Miri functionality: can't call foreign function `foo` on $OS note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/panic/unsupported_syscall.stderr b/tests/panic/unsupported_syscall.stderr index 40ba6671d5..f802159cb1 100644 --- a/tests/panic/unsupported_syscall.stderr +++ b/tests/panic/unsupported_syscall.stderr @@ -1,3 +1,4 @@ thread 'main' panicked at $DIR/unsupported_syscall.rs:LL:CC: unsupported Miri functionality: can't execute syscall with ID 0 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect diff --git a/tests/pass/panic/catch_panic.stderr b/tests/pass/panic/catch_panic.stderr index cbcd626e39..a472a5d80c 100644 --- a/tests/pass/panic/catch_panic.stderr +++ b/tests/pass/panic/catch_panic.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: Hello from std::panic note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect Caught panic message (&str): Hello from std::panic thread 'main' panicked at $DIR/catch_panic.rs:LL:CC: Hello from std::panic: 1 diff --git a/tests/pass/panic/concurrent-panic.stderr b/tests/pass/panic/concurrent-panic.stderr index 0bb3dcd8d2..b2a5cf4922 100644 --- a/tests/pass/panic/concurrent-panic.stderr +++ b/tests/pass/panic/concurrent-panic.stderr @@ -3,6 +3,7 @@ Thread 1 reported it has started thread '' panicked at $DIR/concurrent-panic.rs:LL:CC: panic in thread 2 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect Thread 2 blocking on thread 1 Thread 2 reported it has started Unlocking mutex diff --git a/tests/pass/panic/nested_panic_caught.stderr b/tests/pass/panic/nested_panic_caught.stderr index 4684beb333..3efb4be40f 100644 --- a/tests/pass/panic/nested_panic_caught.stderr +++ b/tests/pass/panic/nested_panic_caught.stderr @@ -1,6 +1,7 @@ thread 'main' panicked at $DIR/nested_panic_caught.rs:LL:CC: once note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'main' panicked at $DIR/nested_panic_caught.rs:LL:CC: twice stack backtrace: diff --git a/tests/pass/panic/thread_panic.stderr b/tests/pass/panic/thread_panic.stderr index badd409d13..bdfe4f98ba 100644 --- a/tests/pass/panic/thread_panic.stderr +++ b/tests/pass/panic/thread_panic.stderr @@ -1,5 +1,6 @@ thread '' panicked at $DIR/thread_panic.rs:LL:CC: Hello! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect thread 'childthread' panicked at $DIR/thread_panic.rs:LL:CC: Hello, world! From a22e35535f341f16035ededf146f9982d895198a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 10:32:44 +0200 Subject: [PATCH 052/208] make realloc with a size of zero fail --- src/shims/foreign_items.rs | 6 ++++-- tests/fail-dep/realloc-zero.rs | 10 ++++++++++ tests/fail-dep/realloc-zero.stderr | 15 +++++++++++++++ tests/pass-dep/malloc.rs | 5 ++--- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 tests/fail-dep/realloc-zero.rs create mode 100644 tests/fail-dep/realloc-zero.stderr diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index acbc36b05b..6a6ad33e52 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -278,6 +278,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let new_align = this.min_align(new_size, kind); if this.ptr_is_null(old_ptr)? { + // Here we must behave like `malloc`. if new_size == 0 { Ok(Pointer::null()) } else { @@ -287,8 +288,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } else { if new_size == 0 { - this.deallocate_ptr(old_ptr, None, kind.into())?; - Ok(Pointer::null()) + // C, in their infinite wisdom, made this UB. + // + throw_ub_format!("`realloc` with a size of zero"); } else { let new_ptr = this.reallocate_ptr( old_ptr, diff --git a/tests/fail-dep/realloc-zero.rs b/tests/fail-dep/realloc-zero.rs new file mode 100644 index 0000000000..1482798e90 --- /dev/null +++ b/tests/fail-dep/realloc-zero.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No libc on Windows + +fn main() { + unsafe { + let p1 = libc::malloc(20); + // C made this UB... + let p2 = libc::realloc(p1, 0); //~ERROR: `realloc` with a size of zero + assert!(p2.is_null()); + } +} diff --git a/tests/fail-dep/realloc-zero.stderr b/tests/fail-dep/realloc-zero.stderr new file mode 100644 index 0000000000..749a61f739 --- /dev/null +++ b/tests/fail-dep/realloc-zero.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `realloc` with a size of zero + --> $DIR/realloc-zero.rs:LL:CC + | +LL | let p2 = libc::realloc(p1, 0); + | ^^^^^^^^^^^^^^^^^^^^ `realloc` with a size of zero + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/realloc-zero.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/pass-dep/malloc.rs b/tests/pass-dep/malloc.rs index f5e014c000..35cd137931 100644 --- a/tests/pass-dep/malloc.rs +++ b/tests/pass-dep/malloc.rs @@ -34,9 +34,8 @@ fn main() { } unsafe { - let p1 = libc::malloc(20); - - let p2 = libc::realloc(p1, 0); + // Realloc with size 0 is okay for the null pointer + let p2 = libc::realloc(ptr::null_mut(), 0); assert!(p2.is_null()); } From c2562d01382ee69094f9706ebd6d3f0cc1cd9484 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 10:42:46 +0200 Subject: [PATCH 053/208] move allocator shim logic into its own file --- src/shims/alloc.rs | 152 +++++++++++++++++++++++++++++ src/shims/foreign_items.rs | 151 +--------------------------- src/shims/mod.rs | 1 + src/shims/unix/foreign_items.rs | 1 + src/shims/unix/fs.rs | 29 +++--- src/shims/windows/foreign_items.rs | 1 + 6 files changed, 178 insertions(+), 157 deletions(-) create mode 100644 src/shims/alloc.rs diff --git a/src/shims/alloc.rs b/src/shims/alloc.rs new file mode 100644 index 0000000000..b5ae06c2a4 --- /dev/null +++ b/src/shims/alloc.rs @@ -0,0 +1,152 @@ +use std::iter; + +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_target::abi::{Align, Size}; + +use crate::*; +use shims::foreign_items::EmulateForeignItemResult; + +/// Check some basic requirements for this allocation request: +/// non-zero size, power-of-two alignment. +pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<'tcx> { + if size == 0 { + throw_ub_format!("creating allocation with size 0"); + } + if !align.is_power_of_two() { + throw_ub_format!("creating allocation with non-power-of-two alignment {}", align); + } + Ok(()) +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Returns the minimum alignment for the target architecture for allocations of the given size. + fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { + let this = self.eval_context_ref(); + // List taken from `library/std/src/sys/pal/common/alloc.rs`. + // This list should be kept in sync with the one from libstd. + let min_align = match this.tcx.sess.target.arch.as_ref() { + "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, + "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => + 16, + arch => bug!("unsupported target architecture for malloc: `{}`", arch), + }; + // Windows always aligns, even small allocations. + // Source: + // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big. + if kind == MiriMemoryKind::WinHeap || size >= min_align { + return Align::from_bytes(min_align).unwrap(); + } + // We have `size < min_align`. Round `size` *down* to the next power of two and use that. + fn prev_power_of_two(x: u64) -> u64 { + let next_pow2 = x.next_power_of_two(); + if next_pow2 == x { + // x *is* a power of two, just use that. + x + } else { + // x is between two powers, so next = 2*prev. + next_pow2 / 2 + } + } + Align::from_bytes(prev_power_of_two(size)).unwrap() + } + + /// Emulates calling the internal __rust_* allocator functions + fn emulate_allocator( + &mut self, + default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, + ) -> InterpResult<'tcx, EmulateForeignItemResult> { + let this = self.eval_context_mut(); + + let Some(allocator_kind) = this.tcx.allocator_kind(()) else { + // in real code, this symbol does not exist without an allocator + return Ok(EmulateForeignItemResult::NotSupported); + }; + + match allocator_kind { + AllocatorKind::Global => { + // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion + // of this attribute. As such we have to call an exported Rust function, + // and not execute any Miri shim. Somewhat unintuitively doing so is done + // by returning `NotSupported`, which triggers the `lookup_exported_symbol` + // fallback case in `emulate_foreign_item`. + return Ok(EmulateForeignItemResult::NotSupported); + } + AllocatorKind::Default => { + default(this)?; + Ok(EmulateForeignItemResult::NeedsJumping) + } + } + } + + fn malloc( + &mut self, + size: u64, + zero_init: bool, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + if size == 0 { + Ok(Pointer::null()) + } else { + let align = this.min_align(size, kind); + let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; + if zero_init { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + ) + .unwrap(); + } + Ok(ptr.into()) + } + } + + fn free( + &mut self, + ptr: Pointer>, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + if !this.ptr_is_null(ptr)? { + this.deallocate_ptr(ptr, None, kind.into())?; + } + Ok(()) + } + + fn realloc( + &mut self, + old_ptr: Pointer>, + new_size: u64, + kind: MiriMemoryKind, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + let new_align = this.min_align(new_size, kind); + if this.ptr_is_null(old_ptr)? { + // Here we must behave like `malloc`. + if new_size == 0 { + Ok(Pointer::null()) + } else { + let new_ptr = + this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?; + Ok(new_ptr.into()) + } + } else { + if new_size == 0 { + // C, in their infinite wisdom, made this UB. + // + throw_ub_format!("`realloc` with a size of zero"); + } else { + let new_ptr = this.reallocate_ptr( + old_ptr, + None, + Size::from_bytes(new_size), + new_align, + kind.into(), + )?; + Ok(new_ptr.into()) + } + } + } +} diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 6a6ad33e52..636361148a 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -1,7 +1,7 @@ use std::{collections::hash_map::Entry, io::Write, iter, path::Path}; use rustc_apfloat::Float; -use rustc_ast::expand::allocator::{alloc_error_handler_name, AllocatorKind}; +use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::{def::DefKind, def_id::CrateNum}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; @@ -12,6 +12,7 @@ use rustc_target::{ spec::abi::Abi, }; +use super::alloc::{check_alloc_request, EvalContextExt as _}; use super::backtrace::EvalContextExt as _; use crate::*; use helpers::{ToHost, ToSoft}; @@ -232,140 +233,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Some(instance) => Ok(Some((this.load_mir(instance.def, None)?, instance))), } } - - fn malloc( - &mut self, - size: u64, - zero_init: bool, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - if size == 0 { - Ok(Pointer::null()) - } else { - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } - Ok(ptr.into()) - } - } - - fn free( - &mut self, - ptr: Pointer>, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - if !this.ptr_is_null(ptr)? { - this.deallocate_ptr(ptr, None, kind.into())?; - } - Ok(()) - } - - fn realloc( - &mut self, - old_ptr: Pointer>, - new_size: u64, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - let new_align = this.min_align(new_size, kind); - if this.ptr_is_null(old_ptr)? { - // Here we must behave like `malloc`. - if new_size == 0 { - Ok(Pointer::null()) - } else { - let new_ptr = - this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?; - Ok(new_ptr.into()) - } - } else { - if new_size == 0 { - // C, in their infinite wisdom, made this UB. - // - throw_ub_format!("`realloc` with a size of zero"); - } else { - let new_ptr = this.reallocate_ptr( - old_ptr, - None, - Size::from_bytes(new_size), - new_align, - kind.into(), - )?; - Ok(new_ptr.into()) - } - } - } } impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Returns the minimum alignment for the target architecture for allocations of the given size. - fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { - let this = self.eval_context_ref(); - // List taken from `library/std/src/sys/pal/common/alloc.rs`. - // This list should be kept in sync with the one from libstd. - let min_align = match this.tcx.sess.target.arch.as_ref() { - "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, - "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => - 16, - arch => bug!("unsupported target architecture for malloc: `{}`", arch), - }; - // Windows always aligns, even small allocations. - // Source: - // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big. - if kind == MiriMemoryKind::WinHeap || size >= min_align { - return Align::from_bytes(min_align).unwrap(); - } - // We have `size < min_align`. Round `size` *down* to the next power of two and use that. - fn prev_power_of_two(x: u64) -> u64 { - let next_pow2 = x.next_power_of_two(); - if next_pow2 == x { - // x *is* a power of two, just use that. - x - } else { - // x is between two powers, so next = 2*prev. - next_pow2 / 2 - } - } - Align::from_bytes(prev_power_of_two(size)).unwrap() - } - - /// Emulates calling the internal __rust_* allocator functions - fn emulate_allocator( - &mut self, - default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { - let this = self.eval_context_mut(); - - let Some(allocator_kind) = this.tcx.allocator_kind(()) else { - // in real code, this symbol does not exist without an allocator - return Ok(EmulateForeignItemResult::NotSupported); - }; - - match allocator_kind { - AllocatorKind::Global => { - // When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion - // of this attribute. As such we have to call an exported Rust function, - // and not execute any Miri shim. Somewhat unintuitively doing so is done - // by returning `NotSupported`, which triggers the `lookup_exported_symbol` - // fallback case in `emulate_foreign_item`. - return Ok(EmulateForeignItemResult::NotSupported); - } - AllocatorKind::Default => { - default(this)?; - Ok(EmulateForeignItemResult::NeedsJumping) - } - } - } - fn emulate_foreign_item_inner( &mut self, link_name: Symbol, @@ -612,7 +483,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; - Self::check_alloc_request(size, align)?; + check_alloc_request(size, align)?; let memory_kind = match link_name.as_str() { "__rust_alloc" => MiriMemoryKind::Rust, @@ -646,7 +517,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; - Self::check_alloc_request(size, align)?; + check_alloc_request(size, align)?; let ptr = this.allocate_ptr( Size::from_bytes(size), @@ -710,7 +581,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let new_size = this.read_target_usize(new_size)?; // No need to check old_size; we anyway check that they match the allocation. - Self::check_alloc_request(new_size, align)?; + check_alloc_request(new_size, align)?; let align = Align::from_bytes(align).unwrap(); let new_ptr = this.reallocate_ptr( @@ -1102,16 +973,4 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // i.e., if we actually emulated the function with one of the shims. Ok(EmulateForeignItemResult::NeedsJumping) } - - /// Check some basic requirements for this allocation request: - /// non-zero size, power-of-two alignment. - fn check_alloc_request(size: u64, align: u64) -> InterpResult<'tcx> { - if size == 0 { - throw_ub_format!("creating allocation with size 0"); - } - if !align.is_power_of_two() { - throw_ub_format!("creating allocation with non-power-of-two alignment {}", align); - } - Ok(()) - } } diff --git a/src/shims/mod.rs b/src/shims/mod.rs index ea6120f757..85c9a202f7 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -1,5 +1,6 @@ #![warn(clippy::arithmetic_side_effects)] +mod alloc; mod backtrace; #[cfg(target_os = "linux")] pub mod ffi_support; diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 3a56aa9138..c72d3bb3df 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -6,6 +6,7 @@ use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; +use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; use crate::*; use shims::foreign_items::EmulateForeignItemResult; diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 31076fdfaf..ebf9f43c19 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -196,13 +196,12 @@ struct OpenDir { read_dir: ReadDir, /// The most recent entry returned by readdir(). /// Will be freed by the next call. - entry: Pointer>, + entry: Option>>, } impl OpenDir { fn new(read_dir: ReadDir) -> Self { - // We rely on `free` being a NOP on null pointers. - Self { read_dir, entry: Pointer::null() } + Self { read_dir, entry: None } } } @@ -924,8 +923,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let d_name_offset = dirent64_layout.fields.offset(4 /* d_name */).bytes(); let size = d_name_offset.checked_add(name_len).unwrap(); - let entry = - this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::Runtime)?; + let entry = this.allocate_ptr( + Size::from_bytes(size), + dirent64_layout.align.abi, + MiriMemoryKind::Runtime.into(), + )?; + let entry: Pointer> = entry.into(); // If the host is a Unix system, fill in the inode number with its real value. // If not, use 0 as a fallback value. @@ -949,23 +952,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?; this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?; - entry + Some(entry) } None => { // end of stream: return NULL - Pointer::null() + None } Some(Err(e)) => { this.set_last_error_from_io_error(e.kind())?; - Pointer::null() + None } }; let open_dir = this.machine.dirs.streams.get_mut(&dirp).unwrap(); let old_entry = std::mem::replace(&mut open_dir.entry, entry); - this.free(old_entry, MiriMemoryKind::Runtime)?; + if let Some(old_entry) = old_entry { + this.deallocate_ptr(old_entry, None, MiriMemoryKind::Runtime.into())?; + } - Ok(Scalar::from_maybe_pointer(entry, this)) + Ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this)) } fn macos_fbsd_readdir_r( @@ -1106,7 +1111,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } if let Some(open_dir) = this.machine.dirs.streams.remove(&dirp) { - this.free(open_dir.entry, MiriMemoryKind::Runtime)?; + if let Some(entry) = open_dir.entry { + this.deallocate_ptr(entry, None, MiriMemoryKind::Runtime.into())?; + } drop(open_dir); Ok(0) } else { diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index de80df3c80..ec4c610148 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -8,6 +8,7 @@ use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use crate::shims::alloc::EvalContextExt as _; use crate::shims::os_str::bytes_to_os_str; use crate::*; use shims::foreign_items::EmulateForeignItemResult; From 0fa941dbe18c545243040c70d9a80a4ff31dff53 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Apr 2024 15:23:00 +0200 Subject: [PATCH 054/208] when an address gets reused, establish a happens-before link in the data race model --- src/alloc_addresses/mod.rs | 42 ++++++++----- src/alloc_addresses/reuse_pool.rs | 39 ++++++++---- src/concurrency/mod.rs | 2 + src/concurrency/thread.rs | 6 ++ src/helpers.rs | 6 +- src/machine.rs | 7 +-- .../address_reuse_happens_before.rs | 60 +++++++++++++++++++ tests/pass/weak_memory/weak.rs | 3 + 8 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 tests/pass/concurrency/address_reuse_happens_before.rs diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 3e00e6037c..50142d6b5a 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -14,7 +14,8 @@ use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; use crate::*; -use reuse_pool::ReusePool; + +use self::reuse_pool::ReusePool; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProvenanceMode { @@ -159,9 +160,12 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert!(!matches!(kind, AllocKind::Dead)); // This allocation does not have a base address yet, pick or reuse one. - let base_addr = if let Some(reuse_addr) = + let base_addr = if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(&mut *rng, size, align) { + if let Some(data_race) = &ecx.machine.data_race { + data_race.validate_lock_acquire(&clock, ecx.get_active_thread()); + } reuse_addr } else { // We have to pick a fresh address. @@ -329,14 +333,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } -impl GlobalStateInner { - pub fn free_alloc_id( - &mut self, - rng: &mut impl Rng, - dead_id: AllocId, - size: Size, - align: Align, - ) { +impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { + pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align) { + let global_state = self.alloc_addresses.get_mut(); + let rng = self.rng.get_mut(); + // We can *not* remove this from `base_addr`, since the interpreter design requires that we // be able to retrieve an AllocId + offset for any memory access *before* we check if the // access is valid. Specifically, `ptr_get_alloc` is called on each attempt at a memory @@ -349,15 +350,26 @@ impl GlobalStateInner { // returns a dead allocation. // To avoid a linear scan we first look up the address in `base_addr`, and then find it in // `int_to_ptr_map`. - let addr = *self.base_addr.get(&dead_id).unwrap(); - let pos = self.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap(); - let removed = self.int_to_ptr_map.remove(pos); + let addr = *global_state.base_addr.get(&dead_id).unwrap(); + let pos = + global_state.int_to_ptr_map.binary_search_by_key(&addr, |(addr, _)| *addr).unwrap(); + let removed = global_state.int_to_ptr_map.remove(pos); assert_eq!(removed, (addr, dead_id)); // double-check that we removed the right thing // We can also remove it from `exposed`, since this allocation can anyway not be returned by // `alloc_id_from_addr` any more. - self.exposed.remove(&dead_id); + global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - self.reuse.add_addr(rng, addr, size, align) + global_state.reuse.add_addr(rng, addr, size, align, || { + let mut clock = concurrency::VClock::default(); + if let Some(data_race) = &self.data_race { + data_race.validate_lock_release( + &mut clock, + self.threads.get_active_thread_id(), + self.threads.active_thread_ref().current_span(), + ); + } + clock + }) } } diff --git a/src/alloc_addresses/reuse_pool.rs b/src/alloc_addresses/reuse_pool.rs index 8374d0ec60..9af4bdac4c 100644 --- a/src/alloc_addresses/reuse_pool.rs +++ b/src/alloc_addresses/reuse_pool.rs @@ -4,6 +4,8 @@ use rand::Rng; use rustc_target::abi::{Align, Size}; +use crate::concurrency::VClock; + const MAX_POOL_SIZE: usize = 64; // Just use fair coins, until we have evidence that other numbers are better. @@ -21,7 +23,10 @@ pub struct ReusePool { /// /// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to /// less than 64 different possible value, that bounds the overall size of the pool. - pool: Vec>, + /// + /// We also store the clock from the thread that donated this pool element, + /// to ensure synchronization with the thread that picks up this address. + pool: Vec>, } impl ReusePool { @@ -29,7 +34,7 @@ impl ReusePool { ReusePool { pool: vec![] } } - fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size)> { + fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, VClock)> { let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap(); if self.pool.len() <= pool_idx { self.pool.resize(pool_idx + 1, Vec::new()); @@ -37,26 +42,38 @@ impl ReusePool { &mut self.pool[pool_idx] } - pub fn add_addr(&mut self, rng: &mut impl Rng, addr: u64, size: Size, align: Align) { + pub fn add_addr( + &mut self, + rng: &mut impl Rng, + addr: u64, + size: Size, + align: Align, + clock: impl FnOnce() -> VClock, + ) { // Let's see if we even want to remember this address. if !rng.gen_bool(ADDR_REMEMBER_CHANCE) { return; } // Determine the pool to add this to, and where in the pool to put it. let subpool = self.subpool(align); - let pos = subpool.partition_point(|(_addr, other_size)| *other_size < size); + let pos = subpool.partition_point(|(_addr, other_size, _)| *other_size < size); // Make sure the pool does not grow too big. if subpool.len() >= MAX_POOL_SIZE { // Pool full. Replace existing element, or last one if this would be even bigger. let clamped_pos = pos.min(subpool.len() - 1); - subpool[clamped_pos] = (addr, size); + subpool[clamped_pos] = (addr, size, clock()); return; } // Add address to pool, at the right position. - subpool.insert(pos, (addr, size)); + subpool.insert(pos, (addr, size, clock())); } - pub fn take_addr(&mut self, rng: &mut impl Rng, size: Size, align: Align) -> Option { + pub fn take_addr( + &mut self, + rng: &mut impl Rng, + size: Size, + align: Align, + ) -> Option<(u64, VClock)> { // Determine whether we'll even attempt a reuse. if !rng.gen_bool(ADDR_TAKE_CHANCE) { return None; @@ -65,9 +82,9 @@ impl ReusePool { let subpool = self.subpool(align); // Let's see if we can find something of the right size. We want to find the full range of // such items, beginning with the first, so we can't use `binary_search_by_key`. - let begin = subpool.partition_point(|(_addr, other_size)| *other_size < size); + let begin = subpool.partition_point(|(_addr, other_size, _)| *other_size < size); let mut end = begin; - while let Some((_addr, other_size)) = subpool.get(end) { + while let Some((_addr, other_size, _)) = subpool.get(end) { if *other_size != size { break; } @@ -80,8 +97,8 @@ impl ReusePool { // Pick a random element with the desired size. let idx = rng.gen_range(begin..end); // Remove it from the pool and return. - let (chosen_addr, chosen_size) = subpool.remove(idx); + let (chosen_addr, chosen_size, clock) = subpool.remove(idx); debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); - Some(chosen_addr) + Some((chosen_addr, clock)) } } diff --git a/src/concurrency/mod.rs b/src/concurrency/mod.rs index 45903107f1..15e1a94d6d 100644 --- a/src/concurrency/mod.rs +++ b/src/concurrency/mod.rs @@ -6,3 +6,5 @@ pub mod init_once; pub mod thread; mod vector_clock; pub mod weak_memory; + +pub use vector_clock::VClock; diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index e28e5f8369..2fabd39a74 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -223,6 +223,12 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> { // empty stacks. self.top_user_relevant_frame.or_else(|| self.stack.len().checked_sub(1)) } + + pub fn current_span(&self) -> Span { + self.top_user_relevant_frame() + .map(|frame_idx| self.stack[frame_idx].current_span()) + .unwrap_or(rustc_span::DUMMY_SP) + } } impl<'mir, 'tcx> std::fmt::Debug for Thread<'mir, 'tcx> { diff --git a/src/helpers.rs b/src/helpers.rs index e2c6769ccb..17ae2dd91a 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1265,9 +1265,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// This function is backed by a cache, and can be assumed to be very fast. /// It will work even when the stack is empty. pub fn current_span(&self) -> Span { - self.top_user_relevant_frame() - .map(|frame_idx| self.stack()[frame_idx].current_span()) - .unwrap_or(rustc_span::DUMMY_SP) + self.threads.active_thread_ref().current_span() } /// Returns the span of the *caller* of the current operation, again @@ -1279,7 +1277,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // We need to go down at least to the caller (len - 2), or however // far we have to go to find a frame in a local crate which is also not #[track_caller]. let frame_idx = self.top_user_relevant_frame().unwrap(); - let frame_idx = cmp::min(frame_idx, self.stack().len().checked_sub(2).unwrap()); + let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2)); self.stack()[frame_idx].current_span() } diff --git a/src/machine.rs b/src/machine.rs index 0bfc59e67d..d7c762fe1a 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1303,12 +1303,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { { *deallocated_at = Some(machine.current_span()); } - machine.alloc_addresses.get_mut().free_alloc_id( - machine.rng.get_mut(), - alloc_id, - size, - align, - ); + machine.free_alloc_id(alloc_id, size, align); Ok(()) } diff --git a/tests/pass/concurrency/address_reuse_happens_before.rs b/tests/pass/concurrency/address_reuse_happens_before.rs new file mode 100644 index 0000000000..c0de1b4826 --- /dev/null +++ b/tests/pass/concurrency/address_reuse_happens_before.rs @@ -0,0 +1,60 @@ +//! Regression test for : +//! When the address gets reused, there should be a happens-before relation. +#![feature(strict_provenance)] +#![feature(sync_unsafe_cell)] + +use std::cell::SyncUnsafeCell; +use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use std::thread; + +static ADDR: AtomicUsize = AtomicUsize::new(0); +static VAL: SyncUnsafeCell = SyncUnsafeCell::new(0); + +fn addr() -> usize { + let alloc = Box::new(42); + <*const i32>::addr(&*alloc) +} + +fn thread1() { + unsafe { + VAL.get().write(42); + } + let alloc = addr(); + ADDR.store(alloc, Relaxed); +} + +fn thread2() -> bool { + // We try to get an allocation at the same address as the global `ADDR`. If we fail too often, + // just bail. `main` will try again with a different allocation. + for _ in 0..16 { + let alloc = addr(); + let addr = ADDR.load(Relaxed); + if alloc == addr { + // We got a reuse! + // If the new allocation is at the same address as the old one, there must be a + // happens-before relationship between them. Therefore, we can read VAL without racing + // and must observe the write above. + let val = unsafe { VAL.get().read() }; + assert_eq!(val, 42); + return true; + } + } + + false +} + +fn main() { + let mut success = false; + while !success { + let t1 = thread::spawn(thread1); + let t2 = thread::spawn(thread2); + t1.join().unwrap(); + success = t2.join().unwrap(); + + // Reset everything. + ADDR.store(0, Relaxed); + unsafe { + VAL.get().write(0); + } + } +} diff --git a/tests/pass/weak_memory/weak.rs b/tests/pass/weak_memory/weak.rs index e10ccc277f..dac63eeeb0 100644 --- a/tests/pass/weak_memory/weak.rs +++ b/tests/pass/weak_memory/weak.rs @@ -37,6 +37,8 @@ fn relaxed() -> bool { let x = static_atomic(0); let j1 = spawn(move || { x.store(1, Relaxed); + // Preemption is disabled, so the store above will never be the + // latest store visible to another thread. x.store(2, Relaxed); }); @@ -138,6 +140,7 @@ fn faa_replaced_by_load() -> bool { } /// Asserts that the function returns true at least once in 100 runs +#[track_caller] fn assert_once(f: fn() -> bool) { assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x)); } From c962d88c8c82edfc6ecbcc00fc19ea1a7283634a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 13:20:01 +0200 Subject: [PATCH 055/208] do not reuse stack addresses; make reuse rate configurable --- README.md | 5 +++++ src/alloc_addresses/mod.rs | 14 +++++++++----- src/alloc_addresses/reuse_pool.rs | 22 ++++++++++++---------- src/bin/miri.rs | 14 ++++++++++++++ src/eval.rs | 3 +++ src/machine.rs | 4 ++-- 6 files changed, 45 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 948f1ee6c6..f01ce52f05 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,11 @@ up the sysroot. If you are using `miri` (the Miri driver) directly, see the Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS` environment variable. We first document the most relevant and most commonly used flags: +* `-Zmiri-address-reuse-rate=` changes the probability that a freed *non-stack* allocation + will be added to the pool for address reuse, and the probability that a new *non-stack* allocation + will be taken from the pool. Stack allocations never get added to or taken from the pool. The + default is `0.5`. Note that a very high reuse rate can mask concurrency bugs as address + reuse induces synchronization between threads. * `-Zmiri-compare-exchange-weak-failure-rate=` changes the failure rate of `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail). You can change it to any value between `0.0` and `1.0`, where `1.0` means it diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 50142d6b5a..247f829297 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -78,7 +78,7 @@ impl GlobalStateInner { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), - reuse: ReusePool::new(), + reuse: ReusePool::new(config.address_reuse_rate), exposed: FxHashSet::default(), next_base_addr: stack_addr, provenance_mode: config.provenance_mode, @@ -142,7 +142,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - fn addr_from_alloc_id(&self, alloc_id: AllocId, _kind: MemoryKind) -> InterpResult<'tcx, u64> { + fn addr_from_alloc_id( + &self, + alloc_id: AllocId, + memory_kind: MemoryKind, + ) -> InterpResult<'tcx, u64> { let ecx = self.eval_context_ref(); let mut global_state = ecx.machine.alloc_addresses.borrow_mut(); let global_state = &mut *global_state; @@ -161,7 +165,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This allocation does not have a base address yet, pick or reuse one. let base_addr = if let Some((reuse_addr, clock)) = - global_state.reuse.take_addr(&mut *rng, size, align) + global_state.reuse.take_addr(&mut *rng, size, align, memory_kind) { if let Some(data_race) = &ecx.machine.data_race { data_race.validate_lock_acquire(&clock, ecx.get_active_thread()); @@ -334,7 +338,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { - pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align) { + pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { let global_state = self.alloc_addresses.get_mut(); let rng = self.rng.get_mut(); @@ -359,7 +363,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // `alloc_id_from_addr` any more. global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - global_state.reuse.add_addr(rng, addr, size, align, || { + global_state.reuse.add_addr(rng, addr, size, align, kind, || { let mut clock = concurrency::VClock::default(); if let Some(data_race) = &self.data_race { data_race.validate_lock_release( diff --git a/src/alloc_addresses/reuse_pool.rs b/src/alloc_addresses/reuse_pool.rs index 9af4bdac4c..3b2f0269eb 100644 --- a/src/alloc_addresses/reuse_pool.rs +++ b/src/alloc_addresses/reuse_pool.rs @@ -4,20 +4,17 @@ use rand::Rng; use rustc_target::abi::{Align, Size}; -use crate::concurrency::VClock; +use crate::{concurrency::VClock, MemoryKind}; const MAX_POOL_SIZE: usize = 64; -// Just use fair coins, until we have evidence that other numbers are better. -const ADDR_REMEMBER_CHANCE: f64 = 0.5; -const ADDR_TAKE_CHANCE: f64 = 0.5; - /// The pool strikes a balance between exploring more possible executions and making it more likely /// to find bugs. The hypothesis is that bugs are more likely to occur when reuse happens for /// allocations with the same layout, since that can trigger e.g. ABA issues in a concurrent data /// structure. Therefore we only reuse allocations when size and alignment match exactly. #[derive(Debug)] pub struct ReusePool { + address_reuse_rate: f64, /// The i-th element in `pool` stores allocations of alignment `2^i`. We store these reusable /// allocations as address-size pairs, the list must be sorted by the size. /// @@ -30,8 +27,8 @@ pub struct ReusePool { } impl ReusePool { - pub fn new() -> Self { - ReusePool { pool: vec![] } + pub fn new(address_reuse_rate: f64) -> Self { + ReusePool { address_reuse_rate, pool: vec![] } } fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, VClock)> { @@ -48,10 +45,14 @@ impl ReusePool { addr: u64, size: Size, align: Align, + kind: MemoryKind, clock: impl FnOnce() -> VClock, ) { // Let's see if we even want to remember this address. - if !rng.gen_bool(ADDR_REMEMBER_CHANCE) { + // We don't remember stack addresses: there's a lot of them (so the perf impact is big), + // and we only want to reuse stack slots within the same thread or else we'll add a lot of + // undesired synchronization. + if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return; } // Determine the pool to add this to, and where in the pool to put it. @@ -73,9 +74,10 @@ impl ReusePool { rng: &mut impl Rng, size: Size, align: Align, + kind: MemoryKind, ) -> Option<(u64, VClock)> { - // Determine whether we'll even attempt a reuse. - if !rng.gen_bool(ADDR_TAKE_CHANCE) { + // Determine whether we'll even attempt a reuse. As above, we don't do reuse for stack addresses. + if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return None; } // Determine the pool to take this from. diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 3f7a965e9d..263a78257f 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -542,6 +542,20 @@ fn main() { miri_config.tracked_alloc_ids.extend(ids); } else if arg == "-Zmiri-track-alloc-accesses" { miri_config.track_alloc_accesses = true; + } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-rate=") { + let rate = match param.parse::() { + Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, + Ok(_) => + show_error!( + "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`" + ), + Err(err) => + show_error!( + "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}", + err + ), + }; + miri_config.address_reuse_rate = rate; } else if let Some(param) = arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=") { let rate = match param.parse::() { Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, diff --git a/src/eval.rs b/src/eval.rs index df0ede1e1b..457fe740d6 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -150,6 +150,8 @@ pub struct MiriConfig { pub page_size: Option, /// Whether to collect a backtrace when each allocation is created, just in case it leaks. pub collect_leak_backtraces: bool, + /// Probability for address reuse. + pub address_reuse_rate: f64, } impl Default for MiriConfig { @@ -186,6 +188,7 @@ impl Default for MiriConfig { num_cpus: 1, page_size: None, collect_leak_backtraces: true, + address_reuse_rate: 0.5, } } } diff --git a/src/machine.rs b/src/machine.rs index d7c762fe1a..eec162a87e 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1282,7 +1282,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra), size: Size, align: Align, - _kind: MemoryKind, + kind: MemoryKind, ) -> InterpResult<'tcx> { if machine.tracked_alloc_ids.contains(&alloc_id) { machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); @@ -1303,7 +1303,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { { *deallocated_at = Some(machine.current_span()); } - machine.free_alloc_id(alloc_id, size, align); + machine.free_alloc_id(alloc_id, size, align, kind); Ok(()) } From d771a719efd90c0c2ef1ceca6563c009dbf8eb0c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 22:17:10 +0200 Subject: [PATCH 056/208] comment clarification and typo fix --- src/shims/os_str.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 0409e31d65..925a35beb6 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -260,6 +260,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_ref(); let target_os = &this.tcx.sess.target.os; + // Below we assume that everything non-Windows works like Unix, at least + // when it comes to file system path conventions. #[cfg(windows)] return if target_os == "windows" { // Windows-on-Windows, all fine. @@ -297,6 +299,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { { converted.remove(0); } + // If the path starts with `\\`, it is a magic Windows path. Conveniently, paths + // starting with `//` on Unix are also magic where the first component can have + // "application-specific" meaning, which is reflected e.g. by `path::absolute` + // leaving leading `//` alone (but normalizing leading `///` to `/`). So we + // don't have to do anything, the magic Windows path should work mostly fine as + // a magic Unix path. } } Cow::Owned(OsString::from_wide(&converted)) @@ -324,13 +332,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { { converted.remove(0); } - // If this start withs a `\` but not a `\\`, then for Windows this is a relative - // path. But the host path is absolute as it started with `/`. We add `\\?` so - // it starts with `\\?\` which is some magic path on Windows that *is* - // considered absolute. + // If this starts withs a `\` but not a `\\`, then for Windows this is a + // relative path (relative to "the root of the current directory", e.g. the + // drive letter). But the host path on Unix is absolute as it starts with `/`. else if converted.get(0).copied() == Some(b'\\') && converted.get(1).copied() != Some(b'\\') { + // We add `\\?` so it starts with `\\?\` which is some magic path on Windows + // that *is* considered absolute. This way we store the absolute host path + // in something that looks like an absolute path to the (Windows) target. converted.splice(0..0, b"\\\\?".iter().copied()); } } From 2155a302a78ee0fc95aecf093ac35739d0cd562b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Apr 2024 22:59:28 +0200 Subject: [PATCH 057/208] when reusing an address, most of the time only reuse from the current thread --- README.md | 9 +- src/alloc_addresses/mod.rs | 17 ++- src/alloc_addresses/reuse_pool.rs | 49 +++++-- src/bin/miri.rs | 129 +++++++----------- src/eval.rs | 3 + .../both_borrows/retag_data_race_write.rs | 2 + tests/fail/data_race/alloc_read_race.rs | 2 + tests/fail/data_race/alloc_write_race.rs | 2 + .../data_race/atomic_read_na_write_race1.rs | 2 + .../data_race/atomic_read_na_write_race2.rs | 2 + .../data_race/atomic_write_na_read_race1.rs | 2 + .../data_race/atomic_write_na_read_race2.rs | 2 + .../data_race/atomic_write_na_write_race1.rs | 2 + .../data_race/atomic_write_na_write_race2.rs | 2 + .../data_race/dangling_thread_async_race.rs | 2 + tests/fail/data_race/dangling_thread_race.rs | 2 + tests/fail/data_race/dealloc_read_race1.rs | 2 + tests/fail/data_race/dealloc_read_race2.rs | 2 + .../fail/data_race/dealloc_read_race_stack.rs | 2 + tests/fail/data_race/dealloc_write_race1.rs | 2 + tests/fail/data_race/dealloc_write_race2.rs | 2 + .../data_race/dealloc_write_race_stack.rs | 2 + .../data_race/enable_after_join_to_main.rs | 2 + tests/fail/data_race/fence_after_load.rs | 3 + tests/fail/data_race/mixed_size_read.rs | 3 + tests/fail/data_race/mixed_size_write.rs | 3 + tests/fail/data_race/read_read_race1.rs | 3 + tests/fail/data_race/read_read_race2.rs | 3 + tests/fail/data_race/read_write_race.rs | 2 + tests/fail/data_race/read_write_race_stack.rs | 2 + tests/fail/data_race/relax_acquire_race.rs | 2 + tests/fail/data_race/release_seq_race.rs | 2 + .../data_race/release_seq_race_same_thread.rs | 2 + tests/fail/data_race/rmw_race.rs | 2 + tests/fail/data_race/stack_pop_race.rs | 3 + tests/fail/data_race/write_write_race.rs | 2 + .../fail/data_race/write_write_race_stack.rs | 2 + .../retag_data_race_protected_read.rs | 3 +- .../stacked_borrows/retag_data_race_read.rs | 3 +- tests/fail/weak_memory/racing_mixed_size.rs | 3 +- .../weak_memory/racing_mixed_size_read.rs | 3 +- .../address_reuse_happens_before.rs | 1 + .../concurrency/disable_data_race_detector.rs | 2 + 43 files changed, 183 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index f01ce52f05..4254b9bb67 100644 --- a/README.md +++ b/README.md @@ -298,8 +298,13 @@ environment variable. We first document the most relevant and most commonly used * `-Zmiri-address-reuse-rate=` changes the probability that a freed *non-stack* allocation will be added to the pool for address reuse, and the probability that a new *non-stack* allocation will be taken from the pool. Stack allocations never get added to or taken from the pool. The - default is `0.5`. Note that a very high reuse rate can mask concurrency bugs as address - reuse induces synchronization between threads. + default is `0.5`. +* `-Zmiri-address-reuse-cross-thread-rate=` changes the probability that an allocation which + attempts to reuse a previously freed block of memory will also consider blocks freed by *other + threads*. The default is `0.1`, which means by default, in 90% of the cases where an address reuse + attempt is made, only addresses from the same thread will be considered. Reusing an address from + another thread induces synchronization between those threads, which can mask data races and weak + memory bugs. * `-Zmiri-compare-exchange-weak-failure-rate=` changes the failure rate of `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail). You can change it to any value between `0.0` and `1.0`, where `1.0` means it diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 247f829297..612649c90c 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -78,7 +78,7 @@ impl GlobalStateInner { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), - reuse: ReusePool::new(config.address_reuse_rate), + reuse: ReusePool::new(config), exposed: FxHashSet::default(), next_base_addr: stack_addr, provenance_mode: config.provenance_mode, @@ -164,9 +164,13 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert!(!matches!(kind, AllocKind::Dead)); // This allocation does not have a base address yet, pick or reuse one. - let base_addr = if let Some((reuse_addr, clock)) = - global_state.reuse.take_addr(&mut *rng, size, align, memory_kind) - { + let base_addr = if let Some((reuse_addr, clock)) = global_state.reuse.take_addr( + &mut *rng, + size, + align, + memory_kind, + ecx.get_active_thread(), + ) { if let Some(data_race) = &ecx.machine.data_race { data_race.validate_lock_acquire(&clock, ecx.get_active_thread()); } @@ -363,12 +367,13 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // `alloc_id_from_addr` any more. global_state.exposed.remove(&dead_id); // Also remember this address for future reuse. - global_state.reuse.add_addr(rng, addr, size, align, kind, || { + let thread = self.threads.get_active_thread_id(); + global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { let mut clock = concurrency::VClock::default(); if let Some(data_race) = &self.data_race { data_race.validate_lock_release( &mut clock, - self.threads.get_active_thread_id(), + thread, self.threads.active_thread_ref().current_span(), ); } diff --git a/src/alloc_addresses/reuse_pool.rs b/src/alloc_addresses/reuse_pool.rs index 3b2f0269eb..fd0b24cc29 100644 --- a/src/alloc_addresses/reuse_pool.rs +++ b/src/alloc_addresses/reuse_pool.rs @@ -4,7 +4,7 @@ use rand::Rng; use rustc_target::abi::{Align, Size}; -use crate::{concurrency::VClock, MemoryKind}; +use crate::{concurrency::VClock, MemoryKind, MiriConfig, ThreadId}; const MAX_POOL_SIZE: usize = 64; @@ -15,23 +15,28 @@ const MAX_POOL_SIZE: usize = 64; #[derive(Debug)] pub struct ReusePool { address_reuse_rate: f64, + address_reuse_cross_thread_rate: f64, /// The i-th element in `pool` stores allocations of alignment `2^i`. We store these reusable - /// allocations as address-size pairs, the list must be sorted by the size. + /// allocations as address-size pairs, the list must be sorted by the size and then the thread ID. /// /// Each of these maps has at most MAX_POOL_SIZE elements, and since alignment is limited to /// less than 64 different possible value, that bounds the overall size of the pool. /// - /// We also store the clock from the thread that donated this pool element, + /// We also store the ID and the data-race clock of the thread that donated this pool element, /// to ensure synchronization with the thread that picks up this address. - pool: Vec>, + pool: Vec>, } impl ReusePool { - pub fn new(address_reuse_rate: f64) -> Self { - ReusePool { address_reuse_rate, pool: vec![] } + pub fn new(config: &MiriConfig) -> Self { + ReusePool { + address_reuse_rate: config.address_reuse_rate, + address_reuse_cross_thread_rate: config.address_reuse_cross_thread_rate, + pool: vec![], + } } - fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, VClock)> { + fn subpool(&mut self, align: Align) -> &mut Vec<(u64, Size, ThreadId, VClock)> { let pool_idx: usize = align.bytes().trailing_zeros().try_into().unwrap(); if self.pool.len() <= pool_idx { self.pool.resize(pool_idx + 1, Vec::new()); @@ -46,6 +51,7 @@ impl ReusePool { size: Size, align: Align, kind: MemoryKind, + thread: ThreadId, clock: impl FnOnce() -> VClock, ) { // Let's see if we even want to remember this address. @@ -55,18 +61,21 @@ impl ReusePool { if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return; } + let clock = clock(); // Determine the pool to add this to, and where in the pool to put it. let subpool = self.subpool(align); - let pos = subpool.partition_point(|(_addr, other_size, _)| *other_size < size); + let pos = subpool.partition_point(|(_addr, other_size, other_thread, _)| { + (*other_size, *other_thread) < (size, thread) + }); // Make sure the pool does not grow too big. if subpool.len() >= MAX_POOL_SIZE { // Pool full. Replace existing element, or last one if this would be even bigger. let clamped_pos = pos.min(subpool.len() - 1); - subpool[clamped_pos] = (addr, size, clock()); + subpool[clamped_pos] = (addr, size, thread, clock); return; } // Add address to pool, at the right position. - subpool.insert(pos, (addr, size, clock())); + subpool.insert(pos, (addr, size, thread, clock)); } pub fn take_addr( @@ -75,21 +84,32 @@ impl ReusePool { size: Size, align: Align, kind: MemoryKind, + thread: ThreadId, ) -> Option<(u64, VClock)> { // Determine whether we'll even attempt a reuse. As above, we don't do reuse for stack addresses. if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return None; } + let cross_thread_reuse = rng.gen_bool(self.address_reuse_cross_thread_rate); // Determine the pool to take this from. let subpool = self.subpool(align); // Let's see if we can find something of the right size. We want to find the full range of - // such items, beginning with the first, so we can't use `binary_search_by_key`. - let begin = subpool.partition_point(|(_addr, other_size, _)| *other_size < size); + // such items, beginning with the first, so we can't use `binary_search_by_key`. If we do + // *not* want to consider other thread's allocations, we effectively use the lexicographic + // order on `(size, thread)`. + let begin = subpool.partition_point(|(_addr, other_size, other_thread, _)| { + *other_size < size + || (*other_size == size && !cross_thread_reuse && *other_thread < thread) + }); let mut end = begin; - while let Some((_addr, other_size, _)) = subpool.get(end) { + while let Some((_addr, other_size, other_thread, _)) = subpool.get(end) { if *other_size != size { break; } + if !cross_thread_reuse && *other_thread != thread { + // We entered the allocations of another thread. + break; + } end += 1; } if end == begin { @@ -99,8 +119,9 @@ impl ReusePool { // Pick a random element with the desired size. let idx = rng.gen_range(begin..end); // Remove it from the pool and return. - let (chosen_addr, chosen_size, clock) = subpool.remove(idx); + let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx); debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); + debug_assert!(cross_thread_reuse || chosen_thread == thread); Some((chosen_addr, clock)) } } diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 263a78257f..db2cd01ce0 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -307,6 +307,15 @@ fn parse_comma_list(input: &str) -> Result, T::Err> { input.split(',').map(str::parse::).collect() } +/// Parses the input as a float in the range from 0.0 to 1.0 (inclusive). +fn parse_rate(input: &str) -> Result { + match input.parse::() { + Ok(rate) if rate >= 0.0 && rate <= 1.0 => Ok(rate), + Ok(_) => Err("must be between `0.0` and `1.0`"), + Err(_) => Err("requires a `f64` between `0.0` and `1.0`"), + } +} + #[cfg(any(target_os = "linux", target_os = "macos"))] fn jemalloc_magic() { // These magic runes are copied from @@ -499,14 +508,9 @@ fn main() { } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") { miri_config.forwarded_env_vars.push(param.to_owned()); } else if let Some(param) = arg.strip_prefix("-Zmiri-track-pointer-tag=") { - let ids: Vec = match parse_comma_list(param) { - Ok(ids) => ids, - Err(err) => - show_error!( - "-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {}", - err - ), - }; + let ids: Vec = parse_comma_list(param).unwrap_or_else(|err| { + show_error!("-Zmiri-track-pointer-tag requires a comma separated list of valid `u64` arguments: {err}") + }); for id in ids.into_iter().map(miri::BorTag::new) { if let Some(id) = id { miri_config.tracked_pointer_tags.insert(id); @@ -515,14 +519,9 @@ fn main() { } } } else if let Some(param) = arg.strip_prefix("-Zmiri-track-call-id=") { - let ids: Vec = match parse_comma_list(param) { - Ok(ids) => ids, - Err(err) => - show_error!( - "-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {}", - err - ), - }; + let ids: Vec = parse_comma_list(param).unwrap_or_else(|err| { + show_error!("-Zmiri-track-call-id requires a comma separated list of valid `u64` arguments: {err}") + }); for id in ids.into_iter().map(miri::CallId::new) { if let Some(id) = id { miri_config.tracked_call_ids.insert(id); @@ -531,70 +530,37 @@ fn main() { } } } else if let Some(param) = arg.strip_prefix("-Zmiri-track-alloc-id=") { - let ids: Vec = match parse_comma_list::>(param) { - Ok(ids) => ids.into_iter().map(miri::AllocId).collect(), - Err(err) => - show_error!( - "-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {}", - err - ), - }; - miri_config.tracked_alloc_ids.extend(ids); + let ids = parse_comma_list::>(param).unwrap_or_else(|err| { + show_error!("-Zmiri-track-alloc-id requires a comma separated list of valid non-zero `u64` arguments: {err}") + }); + miri_config.tracked_alloc_ids.extend(ids.into_iter().map(miri::AllocId)); } else if arg == "-Zmiri-track-alloc-accesses" { miri_config.track_alloc_accesses = true; } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-rate=") { - let rate = match param.parse::() { - Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, - Ok(_) => - show_error!( - "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`" - ), - Err(err) => - show_error!( - "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}", - err - ), - }; - miri_config.address_reuse_rate = rate; + miri_config.address_reuse_rate = parse_rate(param) + .unwrap_or_else(|err| show_error!("-Zmiri-address-reuse-rate {err}")); + } else if let Some(param) = arg.strip_prefix("-Zmiri-address-reuse-cross-thread-rate=") { + miri_config.address_reuse_cross_thread_rate = parse_rate(param) + .unwrap_or_else(|err| show_error!("-Zmiri-address-reuse-cross-thread-rate {err}")); } else if let Some(param) = arg.strip_prefix("-Zmiri-compare-exchange-weak-failure-rate=") { - let rate = match param.parse::() { - Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, - Ok(_) => - show_error!( - "-Zmiri-compare-exchange-weak-failure-rate must be between `0.0` and `1.0`" - ), - Err(err) => - show_error!( - "-Zmiri-compare-exchange-weak-failure-rate requires a `f64` between `0.0` and `1.0`: {}", - err - ), - }; - miri_config.cmpxchg_weak_failure_rate = rate; + miri_config.cmpxchg_weak_failure_rate = parse_rate(param).unwrap_or_else(|err| { + show_error!("-Zmiri-compare-exchange-weak-failure-rate {err}") + }); } else if let Some(param) = arg.strip_prefix("-Zmiri-preemption-rate=") { - let rate = match param.parse::() { - Ok(rate) if rate >= 0.0 && rate <= 1.0 => rate, - Ok(_) => show_error!("-Zmiri-preemption-rate must be between `0.0` and `1.0`"), - Err(err) => - show_error!( - "-Zmiri-preemption-rate requires a `f64` between `0.0` and `1.0`: {}", - err - ), - }; - miri_config.preemption_rate = rate; + miri_config.preemption_rate = + parse_rate(param).unwrap_or_else(|err| show_error!("-Zmiri-preemption-rate {err}")); } else if arg == "-Zmiri-report-progress" { // This makes it take a few seconds between progress reports on my laptop. miri_config.report_progress = Some(1_000_000); } else if let Some(param) = arg.strip_prefix("-Zmiri-report-progress=") { - let interval = match param.parse::() { - Ok(i) => i, - Err(err) => show_error!("-Zmiri-report-progress requires a `u32`: {}", err), - }; + let interval = param.parse::().unwrap_or_else(|err| { + show_error!("-Zmiri-report-progress requires a `u32`: {}", err) + }); miri_config.report_progress = Some(interval); } else if let Some(param) = arg.strip_prefix("-Zmiri-provenance-gc=") { - let interval = match param.parse::() { - Ok(i) => i, - Err(err) => show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err), - }; + let interval = param.parse::().unwrap_or_else(|err| { + show_error!("-Zmiri-provenance-gc requires a `u32`: {}", err) + }); miri_config.gc_interval = interval; } else if let Some(param) = arg.strip_prefix("-Zmiri-measureme=") { miri_config.measureme_out = Some(param.to_string()); @@ -619,23 +585,20 @@ fn main() { show_error!("-Zmiri-extern-so-file `{}` does not exist", filename); } } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") { - let num_cpus = match param.parse::() { - Ok(i) => i, - Err(err) => show_error!("-Zmiri-num-cpus requires a `u32`: {}", err), - }; - + let num_cpus = param + .parse::() + .unwrap_or_else(|err| show_error!("-Zmiri-num-cpus requires a `u32`: {}", err)); miri_config.num_cpus = num_cpus; } else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") { - let page_size = match param.parse::() { - Ok(i) => - if i.is_power_of_two() { - i * 1024 - } else { - show_error!("-Zmiri-force-page-size requires a power of 2: {}", i) - }, - Err(err) => show_error!("-Zmiri-force-page-size requires a `u64`: {}", err), + let page_size = param.parse::().unwrap_or_else(|err| { + show_error!("-Zmiri-force-page-size requires a `u64`: {}", err) + }); + // Convert from kilobytes to bytes. + let page_size = if page_size.is_power_of_two() { + page_size * 1024 + } else { + show_error!("-Zmiri-force-page-size requires a power of 2: {page_size}"); }; - miri_config.page_size = Some(page_size); } else { // Forward to rustc. diff --git a/src/eval.rs b/src/eval.rs index 457fe740d6..45dadb50f4 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -152,6 +152,8 @@ pub struct MiriConfig { pub collect_leak_backtraces: bool, /// Probability for address reuse. pub address_reuse_rate: f64, + /// Probability for address reuse across threads. + pub address_reuse_cross_thread_rate: f64, } impl Default for MiriConfig { @@ -189,6 +191,7 @@ impl Default for MiriConfig { page_size: None, collect_leak_backtraces: true, address_reuse_rate: 0.5, + address_reuse_cross_thread_rate: 0.1, } } } diff --git a/tests/fail/both_borrows/retag_data_race_write.rs b/tests/fail/both_borrows/retag_data_race_write.rs index 3edaf10f3d..0061679eaa 100644 --- a/tests/fail/both_borrows/retag_data_race_write.rs +++ b/tests/fail/both_borrows/retag_data_race_write.rs @@ -1,6 +1,8 @@ //! Make sure that a retag acts like a write for the data race model. //@revisions: stack tree //@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 //@[tree]compile-flags: -Zmiri-tree-borrows #[derive(Copy, Clone)] struct SendPtr(*mut u8); diff --git a/tests/fail/data_race/alloc_read_race.rs b/tests/fail/data_race/alloc_read_race.rs index 2cf3660690..c85c0ebe24 100644 --- a/tests/fail/data_race/alloc_read_race.rs +++ b/tests/fail/data_race/alloc_read_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 #![feature(new_uninit)] use std::mem::MaybeUninit; diff --git a/tests/fail/data_race/alloc_write_race.rs b/tests/fail/data_race/alloc_write_race.rs index e95e0e1a84..9e2a430dd9 100644 --- a/tests/fail/data_race/alloc_write_race.rs +++ b/tests/fail/data_race/alloc_write_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 #![feature(new_uninit)] use std::ptr::null_mut; diff --git a/tests/fail/data_race/atomic_read_na_write_race1.rs b/tests/fail/data_race/atomic_read_na_write_race1.rs index a256267bcd..4003892f0a 100644 --- a/tests/fail/data_race/atomic_read_na_write_race1.rs +++ b/tests/fail/data_race/atomic_read_na_write_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/fail/data_race/atomic_read_na_write_race2.rs b/tests/fail/data_race/atomic_read_na_write_race2.rs index cc6a0742c2..8bceba9380 100644 --- a/tests/fail/data_race/atomic_read_na_write_race2.rs +++ b/tests/fail/data_race/atomic_read_na_write_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/tests/fail/data_race/atomic_write_na_read_race1.rs b/tests/fail/data_race/atomic_write_na_read_race1.rs index 7392781e6c..1a2746a26f 100644 --- a/tests/fail/data_race/atomic_write_na_read_race1.rs +++ b/tests/fail/data_race/atomic_write_na_read_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/tests/fail/data_race/atomic_write_na_read_race2.rs b/tests/fail/data_race/atomic_write_na_read_race2.rs index f681ce0c05..e0876a93fd 100644 --- a/tests/fail/data_race/atomic_write_na_read_race2.rs +++ b/tests/fail/data_race/atomic_write_na_read_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/fail/data_race/atomic_write_na_write_race1.rs b/tests/fail/data_race/atomic_write_na_write_race1.rs index 47a3ef5d16..1010216a49 100644 --- a/tests/fail/data_race/atomic_write_na_write_race1.rs +++ b/tests/fail/data_race/atomic_write_na_write_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/fail/data_race/atomic_write_na_write_race2.rs b/tests/fail/data_race/atomic_write_na_write_race2.rs index 8bba4a8892..b494bd3a00 100644 --- a/tests/fail/data_race/atomic_write_na_write_race2.rs +++ b/tests/fail/data_race/atomic_write_na_write_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; diff --git a/tests/fail/data_race/dangling_thread_async_race.rs b/tests/fail/data_race/dangling_thread_async_race.rs index 5b9005606e..dffafe3cfa 100644 --- a/tests/fail/data_race/dangling_thread_async_race.rs +++ b/tests/fail/data_race/dangling_thread_async_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::mem; use std::thread::{sleep, spawn}; diff --git a/tests/fail/data_race/dangling_thread_race.rs b/tests/fail/data_race/dangling_thread_race.rs index 91c1191e03..8dc35c7ea7 100644 --- a/tests/fail/data_race/dangling_thread_race.rs +++ b/tests/fail/data_race/dangling_thread_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::mem; use std::thread::{sleep, spawn}; diff --git a/tests/fail/data_race/dealloc_read_race1.rs b/tests/fail/data_race/dealloc_read_race1.rs index 5928e47176..f174909e9d 100644 --- a/tests/fail/data_race/dealloc_read_race1.rs +++ b/tests/fail/data_race/dealloc_read_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/dealloc_read_race2.rs b/tests/fail/data_race/dealloc_read_race2.rs index c5f82cc9a7..1edfbf5e61 100644 --- a/tests/fail/data_race/dealloc_read_race2.rs +++ b/tests/fail/data_race/dealloc_read_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/dealloc_read_race_stack.rs b/tests/fail/data_race/dealloc_read_race_stack.rs index 1095f1e4e8..c67e03d362 100644 --- a/tests/fail/data_race/dealloc_read_race_stack.rs +++ b/tests/fail/data_race/dealloc_read_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/tests/fail/data_race/dealloc_write_race1.rs b/tests/fail/data_race/dealloc_write_race1.rs index b5911e5111..7605f1911d 100644 --- a/tests/fail/data_race/dealloc_write_race1.rs +++ b/tests/fail/data_race/dealloc_write_race1.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/dealloc_write_race2.rs b/tests/fail/data_race/dealloc_write_race2.rs index 7a2c882f7e..4f3819bd63 100644 --- a/tests/fail/data_race/dealloc_write_race2.rs +++ b/tests/fail/data_race/dealloc_write_race2.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/dealloc_write_race_stack.rs b/tests/fail/data_race/dealloc_write_race_stack.rs index 5ee4cc04a8..8e63bc1dc7 100644 --- a/tests/fail/data_race/dealloc_write_race_stack.rs +++ b/tests/fail/data_race/dealloc_write_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/tests/fail/data_race/enable_after_join_to_main.rs b/tests/fail/data_race/enable_after_join_to_main.rs index f2da45d727..53050608d2 100644 --- a/tests/fail/data_race/enable_after_join_to_main.rs +++ b/tests/fail/data_race/enable_after_join_to_main.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/fence_after_load.rs b/tests/fail/data_race/fence_after_load.rs index 683e3b9c7a..92cb4ccccf 100644 --- a/tests/fail/data_race/fence_after_load.rs +++ b/tests/fail/data_race/fence_after_load.rs @@ -1,5 +1,8 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{fence, AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; diff --git a/tests/fail/data_race/mixed_size_read.rs b/tests/fail/data_race/mixed_size_read.rs index 091a47070b..61af972b3d 100644 --- a/tests/fail/data_race/mixed_size_read.rs +++ b/tests/fail/data_race/mixed_size_read.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; use std::thread; diff --git a/tests/fail/data_race/mixed_size_write.rs b/tests/fail/data_race/mixed_size_write.rs index 49fb6c1d5c..12e51bb942 100644 --- a/tests/fail/data_race/mixed_size_write.rs +++ b/tests/fail/data_race/mixed_size_write.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; use std::thread; diff --git a/tests/fail/data_race/read_read_race1.rs b/tests/fail/data_race/read_read_race1.rs index f66b5ca3d5..02aa4e4b71 100644 --- a/tests/fail/data_race/read_read_race1.rs +++ b/tests/fail/data_race/read_read_race1.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, Ordering}; use std::thread; diff --git a/tests/fail/data_race/read_read_race2.rs b/tests/fail/data_race/read_read_race2.rs index d87b667d91..3b94c9143f 100644 --- a/tests/fail/data_race/read_read_race2.rs +++ b/tests/fail/data_race/read_read_race2.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0.0 +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::sync::atomic::{AtomicU16, Ordering}; use std::thread; diff --git a/tests/fail/data_race/read_write_race.rs b/tests/fail/data_race/read_write_race.rs index 70971b59ff..adf19dda9d 100644 --- a/tests/fail/data_race/read_write_race.rs +++ b/tests/fail/data_race/read_write_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. Stacked borrows interferes by having its own accesses. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/read_write_race_stack.rs b/tests/fail/data_race/read_write_race_stack.rs index 9fec3ceee0..f411767f7b 100644 --- a/tests/fail/data_race/read_write_race_stack.rs +++ b/tests/fail/data_race/read_write_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/tests/fail/data_race/relax_acquire_race.rs b/tests/fail/data_race/relax_acquire_race.rs index be4450794c..c4f9438082 100644 --- a/tests/fail/data_race/relax_acquire_race.rs +++ b/tests/fail/data_race/relax_acquire_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/fail/data_race/release_seq_race.rs b/tests/fail/data_race/release_seq_race.rs index 9810832413..f03ab3efa0 100644 --- a/tests/fail/data_race/release_seq_race.rs +++ b/tests/fail/data_race/release_seq_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::{sleep, spawn}; diff --git a/tests/fail/data_race/release_seq_race_same_thread.rs b/tests/fail/data_race/release_seq_race_same_thread.rs index 93cbc2a57d..88ae01b3ca 100644 --- a/tests/fail/data_race/release_seq_race_same_thread.rs +++ b/tests/fail/data_race/release_seq_race_same_thread.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/fail/data_race/rmw_race.rs b/tests/fail/data_race/rmw_race.rs index 982e9c1c41..d738caa105 100644 --- a/tests/fail/data_race/rmw_race.rs +++ b/tests/fail/data_race/rmw_race.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread::spawn; diff --git a/tests/fail/data_race/stack_pop_race.rs b/tests/fail/data_race/stack_pop_race.rs index 68d82bc30a..762a8e51f6 100644 --- a/tests/fail/data_race/stack_pop_race.rs +++ b/tests/fail/data_race/stack_pop_race.rs @@ -1,4 +1,7 @@ //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 + use std::thread; #[derive(Copy, Clone)] diff --git a/tests/fail/data_race/write_write_race.rs b/tests/fail/data_race/write_write_race.rs index e8924702af..993d8d25b4 100644 --- a/tests/fail/data_race/write_write_race.rs +++ b/tests/fail/data_race/write_write_race.rs @@ -1,5 +1,7 @@ // We want to control preemption here. //@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; diff --git a/tests/fail/data_race/write_write_race_stack.rs b/tests/fail/data_race/write_write_race_stack.rs index 984ae2ee83..8070a7f4fc 100644 --- a/tests/fail/data_race/write_write_race_stack.rs +++ b/tests/fail/data_race/write_write_race_stack.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-weak-memory-emulation -Zmiri-preemption-rate=0 -Zmiri-disable-stacked-borrows +// Avoid accidental synchronization via address reuse inside `thread::spawn`. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 use std::ptr::null_mut; use std::sync::atomic::{AtomicPtr, Ordering}; diff --git a/tests/fail/stacked_borrows/retag_data_race_protected_read.rs b/tests/fail/stacked_borrows/retag_data_race_protected_read.rs index 3de517055e..a6ee7b40c3 100644 --- a/tests/fail/stacked_borrows/retag_data_race_protected_read.rs +++ b/tests/fail/stacked_borrows/retag_data_race_protected_read.rs @@ -1,4 +1,5 @@ -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 use std::thread; #[derive(Copy, Clone)] diff --git a/tests/fail/stacked_borrows/retag_data_race_read.rs b/tests/fail/stacked_borrows/retag_data_race_read.rs index 25c92ddf6c..949f659e7e 100644 --- a/tests/fail/stacked_borrows/retag_data_race_read.rs +++ b/tests/fail/stacked_borrows/retag_data_race_read.rs @@ -1,5 +1,6 @@ //! Make sure that a retag acts like a read for the data race model. -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 #[derive(Copy, Clone)] struct SendPtr(*mut u8); diff --git a/tests/fail/weak_memory/racing_mixed_size.rs b/tests/fail/weak_memory/racing_mixed_size.rs index dfe9397a4c..1193dddc57 100644 --- a/tests/fail/weak_memory/racing_mixed_size.rs +++ b/tests/fail/weak_memory/racing_mixed_size.rs @@ -1,5 +1,6 @@ // We want to control preemption here. -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 #![feature(core_intrinsics)] diff --git a/tests/fail/weak_memory/racing_mixed_size_read.rs b/tests/fail/weak_memory/racing_mixed_size_read.rs index b946a75c3a..0a0e372f1f 100644 --- a/tests/fail/weak_memory/racing_mixed_size_read.rs +++ b/tests/fail/weak_memory/racing_mixed_size_read.rs @@ -1,5 +1,6 @@ // We want to control preemption here. -//@compile-flags: -Zmiri-preemption-rate=0 +// Avoid accidental synchronization via address reuse. +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 use std::sync::atomic::Ordering::*; use std::sync::atomic::{AtomicU16, AtomicU32}; diff --git a/tests/pass/concurrency/address_reuse_happens_before.rs b/tests/pass/concurrency/address_reuse_happens_before.rs index c0de1b4826..cfc1ef7ae4 100644 --- a/tests/pass/concurrency/address_reuse_happens_before.rs +++ b/tests/pass/concurrency/address_reuse_happens_before.rs @@ -1,5 +1,6 @@ //! Regression test for : //! When the address gets reused, there should be a happens-before relation. +//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=1.0 #![feature(strict_provenance)] #![feature(sync_unsafe_cell)] diff --git a/tests/pass/concurrency/disable_data_race_detector.rs b/tests/pass/concurrency/disable_data_race_detector.rs index 049b5e7f49..354a4bef93 100644 --- a/tests/pass/concurrency/disable_data_race_detector.rs +++ b/tests/pass/concurrency/disable_data_race_detector.rs @@ -1,4 +1,6 @@ //@compile-flags: -Zmiri-disable-data-race-detector +// Avoid non-determinism +//@compile-flags: -Zmiri-preemption-rate=0 -Zmiri-address-reuse-cross-thread-rate=0 use std::thread::spawn; From fd7755f65920753b446cd2ab7ccce8f5e56bd1ca Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 19 Apr 2024 05:05:48 +0000 Subject: [PATCH 058/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index f8f3016b15..f0fa47208a 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -c45dee5efd0c042e9d1e24559ebd0d6424d8aa70 +fa0068b5412baecc932772dda72c0621bfa7ab00 From 8a8fc0433aaa9f31bf75655c06852031510940cb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Apr 2024 09:17:37 +0200 Subject: [PATCH 059/208] share code between win-to-unix and unix-to-win path conversion --- src/shims/os_str.rs | 148 ++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 81 deletions(-) diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 925a35beb6..3e8c35d48a 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -251,7 +251,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.alloc_os_str_as_wide_str(&os_str, memkind) } - #[allow(clippy::get_first)] fn convert_path<'a>( &self, os_str: Cow<'a, OsStr>, @@ -260,6 +259,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_ref(); let target_os = &this.tcx.sess.target.os; + /// Adjust a Windows path to Unix conventions such that it un-does everything that + /// `unix_to_windows` did, and such that if the Windows input path was absolute, then the + /// Unix output path is absolute. + fn windows_to_unix(path: &mut Vec) + where + T: From + Copy + Eq, + { + let sep = T::from(b'/'); + // Make sure all path separators are `/`. + for c in path.iter_mut() { + if *c == b'\\'.into() { + *c = sep; + } + } + // If this starts with `//?/`, it was probably produced by `unix_to_windows`` and we + // remove the `//?` that got added to get the Unix path back out. + if path.get(0..4) == Some(&[sep, sep, b'?'.into(), sep]) { + // Remove first 3 characters. It still starts with `/` so it is absolute on Unix. + path.splice(0..3, std::iter::empty()); + } + // If it starts with a drive letter (`X:/`), convert it to an absolute Unix path. + else if path.get(1..3) == Some(&[b':'.into(), sep]) { + // We add a `/` at the beginning, to store the absolute Windows + // path in something that looks like an absolute Unix path. + path.insert(0, sep); + } + } + + /// Adjust a Unix path to Windows conventions such that it un-does everything that + /// `windows_to_unix` did, and such that if the Unix input path was absolute, then the + /// Windows output path is absolute. + fn unix_to_windows(path: &mut Vec) + where + T: From + Copy + Eq, + { + let sep = T::from(b'\\'); + // Make sure all path separators are `\`. + for c in path.iter_mut() { + if *c == b'/'.into() { + *c = sep; + } + } + // If the path is `\X:\`, the leading separator was probably added by `windows_to_unix` + // and we should get rid of it again. + if path.get(2..4) == Some(&[b':'.into(), sep]) && path[0] == sep { + // The new path is still absolute on Windows. + path.remove(0); + } + // If this starts withs a `\` but not a `\\`, then this was absolute on Unix but is + // relative on Windows (relative to "the root of the current directory", e.g. the + // drive letter). + else if path.first() == Some(&sep) && path.get(1) != Some(&sep) { + // We add `\\?` so it starts with `\\?\` which is some magic path on Windows + // that *is* considered absolute. This way we store the absolute Unix path + // in something that looks like an absolute Windows path. + path.splice(0..0, [sep, sep, b'?'.into()]); + } + } + // Below we assume that everything non-Windows works like Unix, at least // when it comes to file system path conventions. #[cfg(windows)] @@ -268,102 +326,30 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { os_str } else { // Unix target, Windows host. - let (from, to) = match direction { - PathConversion::HostToTarget => ('\\', '/'), - PathConversion::TargetToHost => ('/', '\\'), - }; - let mut converted = os_str - .encode_wide() - .map(|wchar| if wchar == from as u16 { to as u16 } else { wchar }) - .collect::>(); - // We also have to ensure that absolute paths remain absolute. + let mut path: Vec = os_str.encode_wide().collect(); match direction { PathConversion::HostToTarget => { - // If this is an absolute Windows path that starts with a drive letter (`C:/...` - // after separator conversion), it would not be considered absolute by Unix - // target code. - if converted.get(1).copied() == Some(b':' as u16) - && converted.get(2).copied() == Some(b'/' as u16) - { - // We add a `/` at the beginning, to store the absolute Windows - // path in something that looks like an absolute Unix path. - converted.insert(0, b'/' as u16); - } + windows_to_unix(&mut path); } PathConversion::TargetToHost => { - // If the path is `\C:\`, the leading backslash was probably added by the above code - // and we should get rid of it again. - if converted.get(0).copied() == Some(b'\\' as u16) - && converted.get(2).copied() == Some(b':' as u16) - && converted.get(3).copied() == Some(b'\\' as u16) - { - converted.remove(0); - } - // If the path starts with `\\`, it is a magic Windows path. Conveniently, paths - // starting with `//` on Unix are also magic where the first component can have - // "application-specific" meaning, which is reflected e.g. by `path::absolute` - // leaving leading `//` alone (but normalizing leading `///` to `/`). So we - // don't have to do anything, the magic Windows path should work mostly fine as - // a magic Unix path. + unix_to_windows(&mut path); } } - Cow::Owned(OsString::from_wide(&converted)) + Cow::Owned(OsString::from_wide(&path)) }; #[cfg(unix)] return if target_os == "windows" { // Windows target, Unix host. - let (from, to) = match direction { - PathConversion::HostToTarget => (b'/', b'\\'), - PathConversion::TargetToHost => (b'\\', b'/'), - }; - let mut converted = os_str - .as_bytes() - .iter() - .map(|&wchar| if wchar == from { to } else { wchar }) - .collect::>(); - // We also have to ensure that absolute paths remain absolute. + let mut path: Vec = os_str.into_owned().into_encoded_bytes(); match direction { PathConversion::HostToTarget => { - // If the path is `/C:/`, the leading backslash was probably added by the below - // driver letter handling and we should get rid of it again. - if converted.get(0).copied() == Some(b'\\') - && converted.get(2).copied() == Some(b':') - && converted.get(3).copied() == Some(b'\\') - { - converted.remove(0); - } - // If this starts withs a `\` but not a `\\`, then for Windows this is a - // relative path (relative to "the root of the current directory", e.g. the - // drive letter). But the host path on Unix is absolute as it starts with `/`. - else if converted.get(0).copied() == Some(b'\\') - && converted.get(1).copied() != Some(b'\\') - { - // We add `\\?` so it starts with `\\?\` which is some magic path on Windows - // that *is* considered absolute. This way we store the absolute host path - // in something that looks like an absolute path to the (Windows) target. - converted.splice(0..0, b"\\\\?".iter().copied()); - } + unix_to_windows(&mut path); } PathConversion::TargetToHost => { - // If this starts with `//?/`, it was probably produced by the above code and we - // remove the `//?` that got added to get the Unix path back out. - if converted.get(0).copied() == Some(b'/') - && converted.get(1).copied() == Some(b'/') - && converted.get(2).copied() == Some(b'?') - && converted.get(3).copied() == Some(b'/') - { - // Remove first 3 characters - converted.splice(0..3, std::iter::empty()); - } - // If it starts with a drive letter, convert it to an absolute Unix path. - else if converted.get(1).copied() == Some(b':') - && converted.get(2).copied() == Some(b'/') - { - converted.insert(0, b'/'); - } + windows_to_unix(&mut path); } } - Cow::Owned(OsString::from_vec(converted)) + Cow::Owned(OsString::from_vec(path)) } else { // Unix-on-Unix, all is fine. os_str From e76b08bcee619d92b3259d54e4c089b76edb1caa Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Apr 2024 10:59:27 +0200 Subject: [PATCH 060/208] make test not leak rustc crate hash --- tests/panic/mir-validation.rs | 1 + tests/panic/mir-validation.stderr | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/panic/mir-validation.rs b/tests/panic/mir-validation.rs index 5e207c2609..fe618f6c81 100644 --- a/tests/panic/mir-validation.rs +++ b/tests/panic/mir-validation.rs @@ -3,6 +3,7 @@ //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" //@normalize-stderr-test: "\n[ =]*note:.*" -> "" +//@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId" #![feature(custom_mir, core_intrinsics)] use core::intrinsics::mir::*; diff --git a/tests/panic/mir-validation.stderr b/tests/panic/mir-validation.stderr index 243fed020e..d158c996dc 100644 --- a/tests/panic/mir-validation.stderr +++ b/tests/panic/mir-validation.stderr @@ -1,5 +1,5 @@ thread 'rustc' panicked at compiler/rustc_const_eval/src/transform/validate.rs:LL:CC: -broken MIR in Item(DefId(0:4 ~ mir_validation[fad2]::main)) (after phase change to runtime-optimized) at bb0[1]: +broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]: (*(_2.0: *mut i32)), has deref at the wrong place stack backtrace: From 79535236b9f3831f451056ff9b8b95fe3b3a5e01 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 20 Apr 2024 05:05:23 +0000 Subject: [PATCH 061/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index f0fa47208a..a60acf44a4 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -fa0068b5412baecc932772dda72c0621bfa7ab00 +c8d19a92aa9022eb690899cf6d54fd23cb6877e5 From ba5634743ebecab8ce04d6c656d524d36af03a57 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 08:26:42 +0200 Subject: [PATCH 062/208] fix clippy warning --- src/borrow_tracker/tree_borrows/exhaustive.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/borrow_tracker/tree_borrows/exhaustive.rs b/src/borrow_tracker/tree_borrows/exhaustive.rs index daf3590358..d50a22a910 100644 --- a/src/borrow_tracker/tree_borrows/exhaustive.rs +++ b/src/borrow_tracker/tree_borrows/exhaustive.rs @@ -2,7 +2,6 @@ //! (These are used in Tree Borrows `#[test]`s for thorough verification //! of the behavior of the state machine of permissions, //! but the contents of this file are extremely generic) -#![cfg(test)] pub trait Exhaustive: Sized { fn exhaustive() -> Box>; From dfc32c753af71d4fadc42f519a612fb4cb687059 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 08:26:48 +0200 Subject: [PATCH 063/208] re-bless tests --- tests/panic/alloc_error_handler_hook.stderr | 1 + tests/panic/alloc_error_handler_panic.stderr | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/panic/alloc_error_handler_hook.stderr b/tests/panic/alloc_error_handler_hook.stderr index 01c286fe3d..5b309ed09b 100644 --- a/tests/panic/alloc_error_handler_hook.stderr +++ b/tests/panic/alloc_error_handler_hook.stderr @@ -1,4 +1,5 @@ thread 'main' panicked at $DIR/alloc_error_handler_hook.rs:LL:CC: alloc error hook called note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect yes we are unwinding! diff --git a/tests/panic/alloc_error_handler_panic.stderr b/tests/panic/alloc_error_handler_panic.stderr index 202325468b..3d5457799f 100644 --- a/tests/panic/alloc_error_handler_panic.stderr +++ b/tests/panic/alloc_error_handler_panic.stderr @@ -1,4 +1,5 @@ thread 'main' panicked at RUSTLIB/std/src/alloc.rs:LL:CC: memory allocation of 4 bytes failed note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect yes we are unwinding! From 38700bf830c1e9a6095d65e72428710627084034 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 09:01:53 +0200 Subject: [PATCH 064/208] data_race: make the release/acquire API more clear --- src/alloc_addresses/mod.rs | 16 ++++---- src/concurrency/data_race.rs | 71 ++++++++++++++++-------------------- src/concurrency/init_once.rs | 12 +++--- src/concurrency/sync.rs | 62 ++++++++++++------------------- 4 files changed, 67 insertions(+), 94 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index 612649c90c..d59a2cee94 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; -use crate::*; +use crate::{concurrency::VClock, *}; use self::reuse_pool::ReusePool; @@ -172,7 +172,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ecx.get_active_thread(), ) { if let Some(data_race) = &ecx.machine.data_race { - data_race.validate_lock_acquire(&clock, ecx.get_active_thread()); + data_race.acquire_clock(&clock, ecx.get_active_thread()); } reuse_addr } else { @@ -369,15 +369,13 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { // Also remember this address for future reuse. let thread = self.threads.get_active_thread_id(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { - let mut clock = concurrency::VClock::default(); if let Some(data_race) = &self.data_race { - data_race.validate_lock_release( - &mut clock, - thread, - self.threads.active_thread_ref().current_span(), - ); + data_race + .release_clock(thread, self.threads.active_thread_ref().current_span()) + .clone() + } else { + VClock::default() } - clock }) } } diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index 95049b91cb..6c34716b59 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -1701,49 +1701,34 @@ impl GlobalState { format!("thread `{thread_name}`") } - /// Acquire a lock, express that the previous call of - /// `validate_lock_release` must happen before this. + /// Acquire the given clock into the given thread, establishing synchronization with + /// the moment when that clock snapshot was taken via `release_clock`. /// As this is an acquire operation, the thread timestamp is not /// incremented. - pub fn validate_lock_acquire(&self, lock: &VClock, thread: ThreadId) { - let (_, mut clocks) = self.load_thread_state_mut(thread); + pub fn acquire_clock(&self, lock: &VClock, thread: ThreadId) { + let (_, mut clocks) = self.thread_state_mut(thread); clocks.clock.join(lock); } - /// Release a lock handle, express that this happens-before - /// any subsequent calls to `validate_lock_acquire`. - /// For normal locks this should be equivalent to `validate_lock_release_shared` - /// since an acquire operation should have occurred before, however - /// for futex & condvar operations this is not the case and this - /// operation must be used. - pub fn validate_lock_release(&self, lock: &mut VClock, thread: ThreadId, current_span: Span) { - let (index, mut clocks) = self.load_thread_state_mut(thread); - lock.clone_from(&clocks.clock); - clocks.increment_clock(index, current_span); - } - - /// Release a lock handle, express that this happens-before - /// any subsequent calls to `validate_lock_acquire` as well - /// as any previous calls to this function after any - /// `validate_lock_release` calls. - /// For normal locks this should be equivalent to `validate_lock_release`. - /// This function only exists for joining over the set of concurrent readers - /// in a read-write lock and should not be used for anything else. - pub fn validate_lock_release_shared( - &self, - lock: &mut VClock, - thread: ThreadId, - current_span: Span, - ) { - let (index, mut clocks) = self.load_thread_state_mut(thread); - lock.join(&clocks.clock); + /// Returns the `release` clock of the given thread. + /// Other threads can acquire this clock in the future to establish synchronization + /// with this program point. + pub fn release_clock(&self, thread: ThreadId, current_span: Span) -> Ref<'_, VClock> { + // We increment the clock each time this happens, to ensure no two releases + // can be confused with each other. + let (index, mut clocks) = self.thread_state_mut(thread); clocks.increment_clock(index, current_span); + drop(clocks); + // To return a read-only view, we need to release the RefCell + // and borrow it again. + let (_index, clocks) = self.thread_state(thread); + Ref::map(clocks, |c| &c.clock) } /// Load the vector index used by the given thread as well as the set of vector clocks /// used by the thread. #[inline] - fn load_thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { + fn thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { let index = self.thread_info.borrow()[thread] .vector_index .expect("Loading thread state for thread with no assigned vector"); @@ -1752,6 +1737,18 @@ impl GlobalState { (index, clocks) } + /// Load the vector index used by the given thread as well as the set of vector clocks + /// used by the thread. + #[inline] + fn thread_state(&self, thread: ThreadId) -> (VectorIdx, Ref<'_, ThreadClockSet>) { + let index = self.thread_info.borrow()[thread] + .vector_index + .expect("Loading thread state for thread with no assigned vector"); + let ref_vector = self.vector_clocks.borrow(); + let clocks = Ref::map(ref_vector, |vec| &vec[index]); + (index, clocks) + } + /// Load the current vector clock in use and the current set of thread clocks /// in use for the vector. #[inline] @@ -1759,10 +1756,7 @@ impl GlobalState { &self, thread_mgr: &ThreadManager<'_, '_>, ) -> (VectorIdx, Ref<'_, ThreadClockSet>) { - let index = self.current_index(thread_mgr); - let ref_vector = self.vector_clocks.borrow(); - let clocks = Ref::map(ref_vector, |vec| &vec[index]); - (index, clocks) + self.thread_state(thread_mgr.get_active_thread_id()) } /// Load the current vector clock in use and the current set of thread clocks @@ -1772,10 +1766,7 @@ impl GlobalState { &self, thread_mgr: &ThreadManager<'_, '_>, ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { - let index = self.current_index(thread_mgr); - let ref_vector = self.vector_clocks.borrow_mut(); - let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]); - (index, clocks) + self.thread_state_mut(thread_mgr.get_active_thread_id()) } /// Return the current thread, should be the same diff --git a/src/concurrency/init_once.rs b/src/concurrency/init_once.rs index 9dbea08f3e..a01b59c916 100644 --- a/src/concurrency/init_once.rs +++ b/src/concurrency/init_once.rs @@ -41,7 +41,7 @@ pub enum InitOnceStatus { pub(super) struct InitOnce<'mir, 'tcx> { status: InitOnceStatus, waiters: VecDeque>, - data_race: VClock, + clock: VClock, } impl<'mir, 'tcx> VisitProvenance for InitOnce<'mir, 'tcx> { @@ -61,10 +61,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let current_thread = this.get_active_thread(); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire( - &this.machine.threads.sync.init_onces[id].data_race, - current_thread, - ); + data_race + .acquire_clock(&this.machine.threads.sync.init_onces[id].clock, current_thread); } } @@ -176,7 +174,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span); + init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span)); } // Wake up everyone. @@ -202,7 +200,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each complete happens-before the end of the wait if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release(&mut init_once.data_race, current_thread, current_span); + init_once.clock.clone_from(&data_race.release_clock(current_thread, current_span)); } // Wake up one waiting thread, so they can go ahead and try to init this. diff --git a/src/concurrency/sync.rs b/src/concurrency/sync.rs index 0a42871569..d3cef8bf5f 100644 --- a/src/concurrency/sync.rs +++ b/src/concurrency/sync.rs @@ -69,12 +69,8 @@ struct Mutex { lock_count: usize, /// The queue of threads waiting for this mutex. queue: VecDeque, - /// Data race handle. This tracks the happens-before - /// relationship between each mutex access. It is - /// released to during unlock and acquired from during - /// locking, and therefore stores the clock of the last - /// thread to release this mutex. - data_race: VClock, + /// Mutex clock. This tracks the moment of the last unlock. + clock: VClock, } declare_id!(RwLockId); @@ -91,7 +87,7 @@ struct RwLock { writer_queue: VecDeque, /// The queue of reader threads waiting for this lock. reader_queue: VecDeque, - /// Data race handle for writers. Tracks the happens-before + /// Data race clock for writers. Tracks the happens-before /// ordering between each write access to a rwlock and is updated /// after a sequence of concurrent readers to track the happens- /// before ordering between the set of previous readers and @@ -99,8 +95,8 @@ struct RwLock { /// Contains the clock of the last thread to release a writer /// lock or the joined clock of the set of last threads to release /// shared reader locks. - data_race: VClock, - /// Data race handle for readers. This is temporary storage + clock_unlocked: VClock, + /// Data race clock for readers. This is temporary storage /// for the combined happens-before ordering for between all /// concurrent readers and the next writer, and the value /// is stored to the main data_race variable once all @@ -110,7 +106,7 @@ struct RwLock { /// add happens-before orderings between shared reader /// locks. /// This is only relevant when there is an active reader. - data_race_reader: VClock, + clock_current_readers: VClock, } declare_id!(CondvarId); @@ -132,8 +128,8 @@ struct Condvar { /// between a cond-var signal and a cond-var /// wait during a non-spurious signal event. /// Contains the clock of the last thread to - /// perform a futex-signal. - data_race: VClock, + /// perform a condvar-signal. + clock: VClock, } /// The futex state. @@ -145,7 +141,7 @@ struct Futex { /// during a non-spurious wake event. /// Contains the clock of the last thread to /// perform a futex-wake. - data_race: VClock, + clock: VClock, } /// A thread waiting on a futex. @@ -346,7 +342,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } mutex.lock_count = mutex.lock_count.checked_add(1).unwrap(); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire(&mutex.data_race, thread); + data_race.acquire_clock(&mutex.clock, thread); } } @@ -373,11 +369,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // The mutex is completely unlocked. Try transferring ownership // to another thread. if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release( - &mut mutex.data_race, - current_owner, - current_span, - ); + mutex.clock.clone_from(&data_race.release_clock(current_owner, current_span)); } this.mutex_dequeue_and_lock(id); } @@ -448,7 +440,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let count = rwlock.readers.entry(reader).or_insert(0); *count = count.checked_add(1).expect("the reader counter overflowed"); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire(&rwlock.data_race, reader); + data_race.acquire_clock(&rwlock.clock_unlocked, reader); } } @@ -474,20 +466,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } if let Some(data_race) = &this.machine.data_race { // Add this to the shared-release clock of all concurrent readers. - data_race.validate_lock_release_shared( - &mut rwlock.data_race_reader, - reader, - current_span, - ); + rwlock.clock_current_readers.join(&data_race.release_clock(reader, current_span)); } // The thread was a reader. If the lock is not held any more, give it to a writer. if this.rwlock_is_locked(id).not() { // All the readers are finished, so set the writer data-race handle to the value - // of the union of all reader data race handles, since the set of readers - // happen-before the writers + // of the union of all reader data race handles, since the set of readers + // happen-before the writers let rwlock = &mut this.machine.threads.sync.rwlocks[id]; - rwlock.data_race.clone_from(&rwlock.data_race_reader); + rwlock.clock_unlocked.clone_from(&rwlock.clock_current_readers); this.rwlock_dequeue_and_lock_writer(id); } true @@ -511,7 +499,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let rwlock = &mut this.machine.threads.sync.rwlocks[id]; rwlock.writer = Some(writer); if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_acquire(&rwlock.data_race, writer); + data_race.acquire_clock(&rwlock.clock_unlocked, writer); } } @@ -530,11 +518,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { trace!("rwlock_writer_unlock: {:?} unlocked by {:?}", id, expected_writer); // Release memory to next lock holder. if let Some(data_race) = &this.machine.data_race { - data_race.validate_lock_release( - &mut rwlock.data_race, - current_writer, - current_span, - ); + rwlock + .clock_unlocked + .clone_from(&*data_race.release_clock(current_writer, current_span)); } // The thread was a writer. // @@ -611,11 +597,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each condvar signal happens-before the end of the condvar wake if let Some(data_race) = data_race { - data_race.validate_lock_release(&mut condvar.data_race, current_thread, current_span); + condvar.clock.clone_from(&*data_race.release_clock(current_thread, current_span)); } condvar.waiters.pop_front().map(|waiter| { if let Some(data_race) = data_race { - data_race.validate_lock_acquire(&condvar.data_race, waiter.thread); + data_race.acquire_clock(&condvar.clock, waiter.thread); } (waiter.thread, waiter.lock) }) @@ -645,14 +631,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Each futex-wake happens-before the end of the futex wait if let Some(data_race) = data_race { - data_race.validate_lock_release(&mut futex.data_race, current_thread, current_span); + futex.clock.clone_from(&*data_race.release_clock(current_thread, current_span)); } // Wake up the first thread in the queue that matches any of the bits in the bitset. futex.waiters.iter().position(|w| w.bitset & bitset != 0).map(|i| { let waiter = futex.waiters.remove(i).unwrap(); if let Some(data_race) = data_race { - data_race.validate_lock_acquire(&futex.data_race, waiter.thread); + data_race.acquire_clock(&futex.clock, waiter.thread); } waiter.thread }) From 65202a1961107f3fe26811580b87017b9abee58f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 09:08:09 +0200 Subject: [PATCH 065/208] reuse_pool: only do acquire_clock if we reused from a different thread --- src/alloc_addresses/mod.rs | 4 +++- src/alloc_addresses/reuse_pool.rs | 6 ++++-- src/concurrency/vector_clock.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/alloc_addresses/mod.rs b/src/alloc_addresses/mod.rs index d59a2cee94..2bbb34c9a4 100644 --- a/src/alloc_addresses/mod.rs +++ b/src/alloc_addresses/mod.rs @@ -171,7 +171,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { memory_kind, ecx.get_active_thread(), ) { - if let Some(data_race) = &ecx.machine.data_race { + if let Some(clock) = clock + && let Some(data_race) = &ecx.machine.data_race + { data_race.acquire_clock(&clock, ecx.get_active_thread()); } reuse_addr diff --git a/src/alloc_addresses/reuse_pool.rs b/src/alloc_addresses/reuse_pool.rs index fd0b24cc29..77fc9f53f9 100644 --- a/src/alloc_addresses/reuse_pool.rs +++ b/src/alloc_addresses/reuse_pool.rs @@ -78,6 +78,7 @@ impl ReusePool { subpool.insert(pos, (addr, size, thread, clock)); } + /// Returns the address to use and optionally a clock we have to synchronize with. pub fn take_addr( &mut self, rng: &mut impl Rng, @@ -85,7 +86,7 @@ impl ReusePool { align: Align, kind: MemoryKind, thread: ThreadId, - ) -> Option<(u64, VClock)> { + ) -> Option<(u64, Option)> { // Determine whether we'll even attempt a reuse. As above, we don't do reuse for stack addresses. if kind == MemoryKind::Stack || !rng.gen_bool(self.address_reuse_rate) { return None; @@ -122,6 +123,7 @@ impl ReusePool { let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx); debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0); debug_assert!(cross_thread_reuse || chosen_thread == thread); - Some((chosen_addr, clock)) + // No synchronization needed if we reused from the current thread. + Some((chosen_addr, if chosen_thread == thread { None } else { Some(clock) })) } } diff --git a/src/concurrency/vector_clock.rs b/src/concurrency/vector_clock.rs index fe719943dc..0178b01e0b 100644 --- a/src/concurrency/vector_clock.rs +++ b/src/concurrency/vector_clock.rs @@ -152,7 +152,7 @@ impl VClock { } /// Get a mutable slice to the internal vector with minimum `min_len` - /// elements, to preserve invariants this vector must modify + /// elements. To preserve invariants, the caller must modify /// the `min_len`-1 nth element to a non-zero value #[inline] fn get_mut_with_min_len(&mut self, min_len: usize) -> &mut [VTimestamp] { From e5d9fdb8d0e6c9bdba83cf3ef08521289a134e63 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 09:24:22 +0200 Subject: [PATCH 066/208] restrict VClock API surface --- src/concurrency/data_race.rs | 6 ++-- src/concurrency/vector_clock.rs | 64 +++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index 6c34716b59..2281609a04 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -547,9 +547,9 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { - thread_clocks.clock[index].span = current_span; + thread_clocks.clock.index_mut(index).span = current_span; } - thread_clocks.clock[index].set_read_type(read_type); + thread_clocks.clock.index_mut(index).set_read_type(read_type); if self.write_was_before(&thread_clocks.clock) { let race_free = if let Some(atomic) = self.atomic() { // We must be ordered-after all atomic accesses, reads and writes. @@ -577,7 +577,7 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { - thread_clocks.clock[index].span = current_span; + thread_clocks.clock.index_mut(index).span = current_span; } if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock { let race_free = if let Some(atomic) = self.atomic() { diff --git a/src/concurrency/vector_clock.rs b/src/concurrency/vector_clock.rs index 0178b01e0b..2cd3d031b1 100644 --- a/src/concurrency/vector_clock.rs +++ b/src/concurrency/vector_clock.rs @@ -4,7 +4,7 @@ use smallvec::SmallVec; use std::{ cmp::Ordering, fmt::Debug, - ops::{Index, IndexMut, Shr}, + ops::{Index, Shr}, }; use super::data_race::NaReadType; @@ -92,7 +92,7 @@ impl VTimestamp { } #[inline] - pub fn set_read_type(&mut self, read_type: NaReadType) { + pub(super) fn set_read_type(&mut self, read_type: NaReadType) { self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type); } @@ -138,7 +138,7 @@ pub struct VClock(SmallVec<[VTimestamp; SMALL_VECTOR]>); impl VClock { /// Create a new vector-clock containing all zeros except /// for a value at the given index - pub fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock { + pub(super) fn new_with_index(index: VectorIdx, timestamp: VTimestamp) -> VClock { let len = index.index() + 1; let mut vec = smallvec::smallvec![VTimestamp::ZERO; len]; vec[index.index()] = timestamp; @@ -147,10 +147,16 @@ impl VClock { /// Load the internal timestamp slice in the vector clock #[inline] - pub fn as_slice(&self) -> &[VTimestamp] { + pub(super) fn as_slice(&self) -> &[VTimestamp] { + debug_assert!(!self.0.last().is_some_and(|t| t.time() == 0)); self.0.as_slice() } + #[inline] + pub(super) fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp { + self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap() + } + /// Get a mutable slice to the internal vector with minimum `min_len` /// elements. To preserve invariants, the caller must modify /// the `min_len`-1 nth element to a non-zero value @@ -166,7 +172,7 @@ impl VClock { /// Increment the vector clock at a known index /// this will panic if the vector index overflows #[inline] - pub fn increment_index(&mut self, idx: VectorIdx, current_span: Span) { + pub(super) fn increment_index(&mut self, idx: VectorIdx, current_span: Span) { let idx = idx.index(); let mut_slice = self.get_mut_with_min_len(idx + 1); let idx_ref = &mut mut_slice[idx]; @@ -190,28 +196,36 @@ impl VClock { } } - /// Set the element at the current index of the vector - pub fn set_at_index(&mut self, other: &Self, idx: VectorIdx) { + /// Set the element at the current index of the vector. May only increase elements. + pub(super) fn set_at_index(&mut self, other: &Self, idx: VectorIdx) { + let new_timestamp = other[idx]; + // Setting to 0 is different, since the last element cannot be 0. + if new_timestamp.time() == 0 { + if idx.index() >= self.0.len() { + // This index does not even exist yet in our clock. Just do nothing. + return; + } + // This changes an existing element. Since it can only increase, that + // can never make the last element 0. + } + let mut_slice = self.get_mut_with_min_len(idx.index() + 1); + let mut_timestamp = &mut mut_slice[idx.index()]; - let prev_span = mut_slice[idx.index()].span; + let prev_span = mut_timestamp.span; - mut_slice[idx.index()] = other[idx]; + assert!(*mut_timestamp <= new_timestamp, "set_at_index: may only increase the timestamp"); + *mut_timestamp = new_timestamp; - let span = &mut mut_slice[idx.index()].span; + let span = &mut mut_timestamp.span; *span = span.substitute_dummy(prev_span); } /// Set the vector to the all-zero vector #[inline] - pub fn set_zero_vector(&mut self) { + pub(super) fn set_zero_vector(&mut self) { self.0.clear(); } - - /// Return if this vector is the all-zero vector - pub fn is_zero_vector(&self) -> bool { - self.0.is_empty() - } } impl Clone for VClock { @@ -407,13 +421,6 @@ impl Index for VClock { } } -impl IndexMut for VClock { - #[inline] - fn index_mut(&mut self, index: VectorIdx) -> &mut VTimestamp { - self.0.as_mut_slice().get_mut(index.to_u32() as usize).unwrap() - } -} - /// Test vector clock ordering operations /// data-race detection is tested in the external /// test suite @@ -553,4 +560,15 @@ mod tests { "Invalid alt (>=):\n l: {l:?}\n r: {r:?}" ); } + + #[test] + fn set_index_to_0() { + let mut clock1 = from_slice(&[0, 1, 2, 3]); + let clock2 = from_slice(&[0, 2, 3, 4, 0, 5]); + // Naively, this would extend clock1 with a new index and set it to 0, making + // the last index 0. Make sure that does not happen. + clock1.set_at_index(&clock2, VectorIdx(4)); + // This must not have made the last element 0. + assert!(clock1.0.last().unwrap().time() != 0); + } } From ec51a35279fcbd73f352986acffbc3a3acb042af Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 11:18:50 +0200 Subject: [PATCH 067/208] global allocations: don't make up a super-high VectorIdx, just use the main thread --- src/concurrency/data_race.rs | 24 ++++++++++++------------ src/concurrency/thread.rs | 10 +++++++--- src/concurrency/vector_clock.rs | 6 ++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index 2281609a04..bf147093ff 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -847,6 +847,7 @@ impl VClockAlloc { kind: MemoryKind, current_span: Span, ) -> VClockAlloc { + // Determine the thread that did the allocation, and when it did it. let (alloc_timestamp, alloc_index) = match kind { // User allocated and stack memory should track allocation. MemoryKind::Machine( @@ -864,7 +865,7 @@ impl VClockAlloc { (alloc_timestamp, alloc_index) } // Other global memory should trace races but be allocated at the 0 timestamp - // (conceptually they are allocated before everything). + // (conceptually they are allocated on the main thread before everything). MemoryKind::Machine( MiriMemoryKind::Global | MiriMemoryKind::Machine @@ -872,7 +873,8 @@ impl VClockAlloc { | MiriMemoryKind::ExternStatic | MiriMemoryKind::Tls, ) - | MemoryKind::CallerLocation => (VTimestamp::ZERO, VectorIdx::MAX_INDEX), + | MemoryKind::CallerLocation => + (VTimestamp::ZERO, global.thread_index(ThreadId::MAIN_THREAD)), }; VClockAlloc { alloc_ranges: RefCell::new(RangeMap::new( @@ -1454,7 +1456,7 @@ impl GlobalState { // Setup the main-thread since it is not explicitly created: // uses vector index and thread-id 0. let index = global_state.vector_clocks.get_mut().push(ThreadClockSet::default()); - global_state.vector_info.get_mut().push(ThreadId::new(0)); + global_state.vector_info.get_mut().push(ThreadId::MAIN_THREAD); global_state .thread_info .get_mut() @@ -1725,13 +1727,15 @@ impl GlobalState { Ref::map(clocks, |c| &c.clock) } + fn thread_index(&self, thread: ThreadId) -> VectorIdx { + self.thread_info.borrow()[thread].vector_index.expect("thread has no assigned vector") + } + /// Load the vector index used by the given thread as well as the set of vector clocks /// used by the thread. #[inline] fn thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { - let index = self.thread_info.borrow()[thread] - .vector_index - .expect("Loading thread state for thread with no assigned vector"); + let index = self.thread_index(thread); let ref_vector = self.vector_clocks.borrow_mut(); let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]); (index, clocks) @@ -1741,9 +1745,7 @@ impl GlobalState { /// used by the thread. #[inline] fn thread_state(&self, thread: ThreadId) -> (VectorIdx, Ref<'_, ThreadClockSet>) { - let index = self.thread_info.borrow()[thread] - .vector_index - .expect("Loading thread state for thread with no assigned vector"); + let index = self.thread_index(thread); let ref_vector = self.vector_clocks.borrow(); let clocks = Ref::map(ref_vector, |vec| &vec[index]); (index, clocks) @@ -1774,9 +1776,7 @@ impl GlobalState { #[inline] fn current_index(&self, thread_mgr: &ThreadManager<'_, '_>) -> VectorIdx { let active_thread_id = thread_mgr.get_active_thread_id(); - self.thread_info.borrow()[active_thread_id] - .vector_index - .expect("active thread has no assigned vector") + self.thread_index(active_thread_id) } // SC ATOMIC STORE rule in the paper. diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index 2fabd39a74..0116bd0281 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -57,6 +57,8 @@ impl ThreadId { pub fn to_u32(self) -> u32 { self.0 } + + pub const MAIN_THREAD: ThreadId = ThreadId(0); } impl Idx for ThreadId { @@ -401,7 +403,7 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> { // Create the main thread and add it to the list of threads. threads.push(Thread::new(Some("main"), None)); Self { - active_thread: ThreadId::new(0), + active_thread: ThreadId::MAIN_THREAD, threads, sync: SynchronizationState::default(), thread_local_alloc_ids: Default::default(), @@ -416,10 +418,12 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> { ecx: &mut MiriInterpCx<'mir, 'tcx>, on_main_stack_empty: StackEmptyCallback<'mir, 'tcx>, ) { - ecx.machine.threads.threads[ThreadId::new(0)].on_stack_empty = Some(on_main_stack_empty); + ecx.machine.threads.threads[ThreadId::MAIN_THREAD].on_stack_empty = + Some(on_main_stack_empty); if ecx.tcx.sess.target.os.as_ref() != "windows" { // The main thread can *not* be joined on except on windows. - ecx.machine.threads.threads[ThreadId::new(0)].join_status = ThreadJoinStatus::Detached; + ecx.machine.threads.threads[ThreadId::MAIN_THREAD].join_status = + ThreadJoinStatus::Detached; } } diff --git a/src/concurrency/vector_clock.rs b/src/concurrency/vector_clock.rs index 2cd3d031b1..f86f06a9d2 100644 --- a/src/concurrency/vector_clock.rs +++ b/src/concurrency/vector_clock.rs @@ -13,15 +13,13 @@ use super::data_race::NaReadType; /// but in some cases one vector index may be shared with /// multiple thread ids if it's safe to do so. #[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct VectorIdx(u32); +pub(super) struct VectorIdx(u32); impl VectorIdx { #[inline(always)] - pub fn to_u32(self) -> u32 { + fn to_u32(self) -> u32 { self.0 } - - pub const MAX_INDEX: VectorIdx = VectorIdx(u32::MAX); } impl Idx for VectorIdx { From 184878f7f050286f15f09c04a45b4babe8288845 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 11:34:41 +0200 Subject: [PATCH 068/208] more consistently talk about the 'active thread', not the 'current thread' --- src/concurrency/data_race.rs | 44 ++++++++++++++++----------------- src/concurrency/vector_clock.rs | 6 ++--- src/concurrency/weak_memory.rs | 8 +++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/concurrency/data_race.rs b/src/concurrency/data_race.rs index bf147093ff..f2bec972b1 100644 --- a/src/concurrency/data_race.rs +++ b/src/concurrency/data_race.rs @@ -859,7 +859,7 @@ impl VClockAlloc { | MiriMemoryKind::Mmap, ) | MemoryKind::Stack => { - let (alloc_index, clocks) = global.current_thread_state(thread_mgr); + let (alloc_index, clocks) = global.active_thread_state(thread_mgr); let mut alloc_timestamp = clocks.clock[alloc_index]; alloc_timestamp.span = current_span; (alloc_timestamp, alloc_index) @@ -932,7 +932,7 @@ impl VClockAlloc { ptr_dbg: Pointer, ty: Option>, ) -> InterpResult<'tcx> { - let (current_index, current_clocks) = global.current_thread_state(thread_mgr); + let (active_index, active_clocks) = global.active_thread_state(thread_mgr); let mut other_size = None; // if `Some`, this was a size-mismatch race let write_clock; let (other_access, other_thread, other_clock) = @@ -941,30 +941,30 @@ impl VClockAlloc { // we are reporting races between two non-atomic reads. if !access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && - let Some(idx) = Self::find_gt_index(&atomic.write_vector, ¤t_clocks.clock) + let Some(idx) = Self::find_gt_index(&atomic.write_vector, &active_clocks.clock) { (AccessType::AtomicStore, idx, &atomic.write_vector) } else if !access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && - let Some(idx) = Self::find_gt_index(&atomic.read_vector, ¤t_clocks.clock) + let Some(idx) = Self::find_gt_index(&atomic.read_vector, &active_clocks.clock) { (AccessType::AtomicLoad, idx, &atomic.read_vector) // Then check races with non-atomic writes/reads. - } else if mem_clocks.write.1 > current_clocks.clock[mem_clocks.write.0] { + } else if mem_clocks.write.1 > active_clocks.clock[mem_clocks.write.0] { write_clock = mem_clocks.write(); (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock) - } else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, ¤t_clocks.clock) { + } else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, &active_clocks.clock) { (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read) // Finally, mixed-size races. } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size { // This is only a race if we are not synchronized with all atomic accesses, so find // the one we are not synchronized with. other_size = Some(atomic.size); - if let Some(idx) = Self::find_gt_index(&atomic.write_vector, ¤t_clocks.clock) + if let Some(idx) = Self::find_gt_index(&atomic.write_vector, &active_clocks.clock) { (AccessType::AtomicStore, idx, &atomic.write_vector) } else if let Some(idx) = - Self::find_gt_index(&atomic.read_vector, ¤t_clocks.clock) + Self::find_gt_index(&atomic.read_vector, &active_clocks.clock) { (AccessType::AtomicLoad, idx, &atomic.read_vector) } else { @@ -977,7 +977,7 @@ impl VClockAlloc { }; // Load elaborated thread information about the racing thread actions. - let current_thread_info = global.print_thread_metadata(thread_mgr, current_index); + let active_thread_info = global.print_thread_metadata(thread_mgr, active_index); let other_thread_info = global.print_thread_metadata(thread_mgr, other_thread); let involves_non_atomic = !access.is_atomic() || !other_access.is_atomic(); @@ -1005,8 +1005,8 @@ impl VClockAlloc { }, op2: RacingOp { action: access.description(ty, other_size.map(|_| access_size)), - thread_info: current_thread_info, - span: current_clocks.clock.as_slice()[current_index.index()].span_data(), + thread_info: active_thread_info, + span: active_clocks.clock.as_slice()[active_index.index()].span_data(), }, }))? } @@ -1028,7 +1028,7 @@ impl VClockAlloc { let current_span = machine.current_span(); let global = machine.data_race.as_ref().unwrap(); if global.race_detecting() { - let (index, mut thread_clocks) = global.current_thread_state_mut(&machine.threads); + let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); let mut alloc_ranges = self.alloc_ranges.borrow_mut(); for (mem_clocks_range, mem_clocks) in alloc_ranges.iter_mut(access_range.start, access_range.size) @@ -1071,7 +1071,7 @@ impl VClockAlloc { let current_span = machine.current_span(); let global = machine.data_race.as_mut().unwrap(); if global.race_detecting() { - let (index, mut thread_clocks) = global.current_thread_state_mut(&machine.threads); + let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads); for (mem_clocks_range, mem_clocks) in self.alloc_ranges.get_mut().iter_mut(access_range.start, access_range.size) { @@ -1520,7 +1520,7 @@ impl GlobalState { thread: ThreadId, current_span: Span, ) { - let current_index = self.current_index(thread_mgr); + let current_index = self.active_thread_index(thread_mgr); // Enable multi-threaded execution, there are now at least two threads // so data-races are now possible. @@ -1644,7 +1644,7 @@ impl GlobalState { /// `thread_joined`. #[inline] pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_, '_>, current_span: Span) { - let current_index = self.current_index(thread_mgr); + let current_index = self.active_thread_index(thread_mgr); // Increment the clock to a unique termination timestamp. let vector_clocks = self.vector_clocks.get_mut(); @@ -1682,9 +1682,9 @@ impl GlobalState { op: impl FnOnce(VectorIdx, RefMut<'_, ThreadClockSet>) -> InterpResult<'tcx, bool>, ) -> InterpResult<'tcx> { if self.multi_threaded.get() { - let (index, clocks) = self.current_thread_state_mut(thread_mgr); + let (index, clocks) = self.active_thread_state_mut(thread_mgr); if op(index, clocks)? { - let (_, mut clocks) = self.current_thread_state_mut(thread_mgr); + let (_, mut clocks) = self.active_thread_state_mut(thread_mgr); clocks.increment_clock(index, current_span); } } @@ -1754,7 +1754,7 @@ impl GlobalState { /// Load the current vector clock in use and the current set of thread clocks /// in use for the vector. #[inline] - pub(super) fn current_thread_state( + pub(super) fn active_thread_state( &self, thread_mgr: &ThreadManager<'_, '_>, ) -> (VectorIdx, Ref<'_, ThreadClockSet>) { @@ -1764,7 +1764,7 @@ impl GlobalState { /// Load the current vector clock in use and the current set of thread clocks /// in use for the vector mutably for modification. #[inline] - pub(super) fn current_thread_state_mut( + pub(super) fn active_thread_state_mut( &self, thread_mgr: &ThreadManager<'_, '_>, ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) { @@ -1774,20 +1774,20 @@ impl GlobalState { /// Return the current thread, should be the same /// as the data-race active thread. #[inline] - fn current_index(&self, thread_mgr: &ThreadManager<'_, '_>) -> VectorIdx { + fn active_thread_index(&self, thread_mgr: &ThreadManager<'_, '_>) -> VectorIdx { let active_thread_id = thread_mgr.get_active_thread_id(); self.thread_index(active_thread_id) } // SC ATOMIC STORE rule in the paper. pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_, '_>) { - let (index, clocks) = self.current_thread_state(thread_mgr); + let (index, clocks) = self.active_thread_state(thread_mgr); self.last_sc_write.borrow_mut().set_at_index(&clocks.clock, index); } // SC ATOMIC READ rule in the paper. pub(super) fn sc_read(&self, thread_mgr: &ThreadManager<'_, '_>) { - let (.., mut clocks) = self.current_thread_state_mut(thread_mgr); + let (.., mut clocks) = self.active_thread_state_mut(thread_mgr); clocks.read_seqcst.join(&self.last_sc_fence.borrow()); } } diff --git a/src/concurrency/vector_clock.rs b/src/concurrency/vector_clock.rs index f86f06a9d2..c3496bc1a0 100644 --- a/src/concurrency/vector_clock.rs +++ b/src/concurrency/vector_clock.rs @@ -49,7 +49,7 @@ const SMALL_VECTOR: usize = 4; /// a 32-bit unsigned integer which is the actual timestamp, and a `Span` /// so that diagnostics can report what code was responsible for an operation. #[derive(Clone, Copy, Debug)] -pub struct VTimestamp { +pub(super) struct VTimestamp { /// The lowest bit indicates read type, the rest is the time. /// `1` indicates a retag read, `0` a regular read. time_and_read_type: u32, @@ -85,7 +85,7 @@ impl VTimestamp { } #[inline] - pub fn read_type(&self) -> NaReadType { + pub(super) fn read_type(&self) -> NaReadType { if self.time_and_read_type & 1 == 0 { NaReadType::Read } else { NaReadType::Retag } } @@ -95,7 +95,7 @@ impl VTimestamp { } #[inline] - pub fn span_data(&self) -> SpanData { + pub(super) fn span_data(&self) -> SpanData { self.span.data() } } diff --git a/src/concurrency/weak_memory.rs b/src/concurrency/weak_memory.rs index 9ebb64afd3..f544393cfe 100644 --- a/src/concurrency/weak_memory.rs +++ b/src/concurrency/weak_memory.rs @@ -270,7 +270,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { ) { let store_elem = self.buffer.back(); if let Some(store_elem) = store_elem { - let (index, clocks) = global.current_thread_state(thread_mgr); + let (index, clocks) = global.active_thread_state(thread_mgr); store_elem.load_impl(index, &clocks, is_seqcst); } } @@ -289,7 +289,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { let (store_elem, recency) = { // The `clocks` we got here must be dropped before calling validate_atomic_load // as the race detector will update it - let (.., clocks) = global.current_thread_state(thread_mgr); + let (.., clocks) = global.active_thread_state(thread_mgr); // Load from a valid entry in the store buffer self.fetch_store(is_seqcst, &clocks, &mut *rng) }; @@ -300,7 +300,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { // requires access to ThreadClockSet.clock, which is updated by the race detector validate()?; - let (index, clocks) = global.current_thread_state(thread_mgr); + let (index, clocks) = global.active_thread_state(thread_mgr); let loaded = store_elem.load_impl(index, &clocks, is_seqcst); Ok((loaded, recency)) } @@ -312,7 +312,7 @@ impl<'mir, 'tcx: 'mir> StoreBuffer { thread_mgr: &ThreadManager<'_, '_>, is_seqcst: bool, ) -> InterpResult<'tcx> { - let (index, clocks) = global.current_thread_state(thread_mgr); + let (index, clocks) = global.active_thread_state(thread_mgr); self.store_impl(val, index, &clocks.clock, is_seqcst); Ok(()) From da5dfc9cf676191f990edaacd24b6a4ada8b8790 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 20 Apr 2024 11:42:10 +0200 Subject: [PATCH 069/208] ensure the ICE-to-file logic does not affect our test --- tests/panic/mir-validation.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/panic/mir-validation.rs b/tests/panic/mir-validation.rs index fe618f6c81..5c1519f2eb 100644 --- a/tests/panic/mir-validation.rs +++ b/tests/panic/mir-validation.rs @@ -1,4 +1,5 @@ //! Ensure that the MIR validator runs on Miri's input. +//@rustc-env:RUSTC_ICE=0 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" //@normalize-stderr-test: "\n +at [^\n]+" -> "" //@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" From 019f5a2eaa6ac0536212e53e521e005783be4a5f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 21 Apr 2024 10:26:28 +0200 Subject: [PATCH 070/208] the mir-validation ICE test behaves strangely on Windows hosts let's just disable it there, this code is not platform-dependent anyway --- src/diagnostics.rs | 4 ++-- tests/panic/mir-validation.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index d44d04e9bf..0c0ac4c603 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -291,7 +291,7 @@ pub fn report_error<'tcx, 'mir>( ValidationErrorKind::PointerAsInt { .. } | ValidationErrorKind::PartialPointer ) => { - ecx.handle_ice(); // print interpreter backtrace + ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This validation error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e) @@ -308,7 +308,7 @@ pub fn report_error<'tcx, 'mir>( InvalidProgramInfo::AlreadyReported(_) | InvalidProgramInfo::Layout(..), ) => "post-monomorphization error", _ => { - ecx.handle_ice(); // print interpreter backtrace + ecx.handle_ice(); // print interpreter backtrace (this is outside the eval `catch_unwind`) bug!( "This error should be impossible in Miri: {}", format_interp_error(ecx.tcx.dcx(), e) diff --git a/tests/panic/mir-validation.rs b/tests/panic/mir-validation.rs index 5c1519f2eb..f1d0ccc7d0 100644 --- a/tests/panic/mir-validation.rs +++ b/tests/panic/mir-validation.rs @@ -1,10 +1,13 @@ //! Ensure that the MIR validator runs on Miri's input. //@rustc-env:RUSTC_ICE=0 -//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" -//@normalize-stderr-test: "\n +at [^\n]+" -> "" -//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\]" -> "" +//@normalize-stderr-test: "\n +[0-9]+:.+" -> "" +//@normalize-stderr-test: "\n +at .+" -> "" +//@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\].*" -> "" //@normalize-stderr-test: "\n[ =]*note:.*" -> "" //@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId" +// Somehow on rustc Windows CI, the "Miri caused an ICE" message is not shown +// and we don't even get a regular panic; rustc aborts with a different exit code instead. +//@ignore-host-windows #![feature(custom_mir, core_intrinsics)] use core::intrinsics::mir::*; From 338de696051a36c428b6347d07f078930996da07 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 21 Apr 2024 11:35:02 +0200 Subject: [PATCH 071/208] Miri: detect wrong vtables in wide pointers --- tests/fail/dyn-call-trait-mismatch.rs | 5 ++- tests/fail/dyn-call-trait-mismatch.stderr | 4 +- tests/fail/dyn-upcast-nop-wrong-trait.rs | 15 +++++++ tests/fail/dyn-upcast-nop-wrong-trait.stderr | 15 +++++++ tests/fail/dyn-upcast-trait-mismatch.rs | 9 ++-- tests/fail/dyn-upcast-trait-mismatch.stderr | 6 +-- .../fail/validity/wrong-dyn-trait-generic.rs | 12 ++++++ .../validity/wrong-dyn-trait-generic.stderr | 15 +++++++ tests/fail/validity/wrong-dyn-trait.rs | 6 +++ tests/fail/validity/wrong-dyn-trait.stderr | 15 +++++++ tests/pass/cast-rfc0401-vtable-kinds.rs | 2 +- tests/pass/dyn-upcast.rs | 42 ++++++++++--------- 12 files changed, 116 insertions(+), 30 deletions(-) create mode 100644 tests/fail/dyn-upcast-nop-wrong-trait.rs create mode 100644 tests/fail/dyn-upcast-nop-wrong-trait.stderr create mode 100644 tests/fail/validity/wrong-dyn-trait-generic.rs create mode 100644 tests/fail/validity/wrong-dyn-trait-generic.stderr create mode 100644 tests/fail/validity/wrong-dyn-trait.rs create mode 100644 tests/fail/validity/wrong-dyn-trait.stderr diff --git a/tests/fail/dyn-call-trait-mismatch.rs b/tests/fail/dyn-call-trait-mismatch.rs index 13f913454d..f71df9a1c9 100644 --- a/tests/fail/dyn-call-trait-mismatch.rs +++ b/tests/fail/dyn-call-trait-mismatch.rs @@ -1,3 +1,6 @@ +// Validation stops this too early. +//@compile-flags: -Zmiri-disable-validation + trait T1 { #[allow(dead_code)] fn method1(self: Box); @@ -13,5 +16,5 @@ impl T1 for i32 { fn main() { let r = Box::new(0) as Box; let r2: Box = unsafe { std::mem::transmute(r) }; - r2.method2(); //~ERROR: call on a pointer whose vtable does not match its type + r2.method2(); //~ERROR: using vtable for trait `T1` but trait `T2` was expected } diff --git a/tests/fail/dyn-call-trait-mismatch.stderr b/tests/fail/dyn-call-trait-mismatch.stderr index 365186bcc4..019a55bcdc 100644 --- a/tests/fail/dyn-call-trait-mismatch.stderr +++ b/tests/fail/dyn-call-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: `dyn` call on a pointer whose vtable does not match its type +error: Undefined Behavior: using vtable for trait `T1` but trait `T2` was expected --> $DIR/dyn-call-trait-mismatch.rs:LL:CC | LL | r2.method2(); - | ^^^^^^^^^^^^ `dyn` call on a pointer whose vtable does not match its type + | ^^^^^^^^^^^^ using vtable for trait `T1` but trait `T2` was expected | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/fail/dyn-upcast-nop-wrong-trait.rs b/tests/fail/dyn-upcast-nop-wrong-trait.rs new file mode 100644 index 0000000000..dff5a21c4c --- /dev/null +++ b/tests/fail/dyn-upcast-nop-wrong-trait.rs @@ -0,0 +1,15 @@ +// This upcast is currently forbidden because it involves an invalid value. +// However, if in the future we relax the validity requirements for raw pointer vtables, +// we could consider allowing this again -- the cast itself isn't doing anything wrong, +// only the transmutes needed to set up the testcase are wrong. + +use std::fmt; + +fn main() { + // vtable_mismatch_nop_cast + let ptr: &dyn fmt::Display = &0; + let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; //~ERROR: wrong trait + // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed + // vtable so it should still be allowed -- if we ever allow the line above. + let _ptr2 = ptr as *const dyn fmt::Debug; +} diff --git a/tests/fail/dyn-upcast-nop-wrong-trait.stderr b/tests/fail/dyn-upcast-nop-wrong-trait.stderr new file mode 100644 index 0000000000..4165d5ea15 --- /dev/null +++ b/tests/fail/dyn-upcast-nop-wrong-trait.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display` + --> $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC + | +LL | let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug + std::marker::Send + std::marker::Sync`, but encountered `std::fmt::Display` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/dyn-upcast-nop-wrong-trait.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/fail/dyn-upcast-trait-mismatch.rs b/tests/fail/dyn-upcast-trait-mismatch.rs index 982f33b0a3..1d6b677703 100644 --- a/tests/fail/dyn-upcast-trait-mismatch.rs +++ b/tests/fail/dyn-upcast-trait-mismatch.rs @@ -1,3 +1,6 @@ +// Validation stops this too early. +//@compile-flags: -Zmiri-disable-validation + #![feature(trait_upcasting)] #![allow(incomplete_features)] @@ -57,7 +60,7 @@ impl Baz for i32 { fn main() { let baz: &dyn Baz = &1; - let baz_fake: &dyn Bar = unsafe { std::mem::transmute(baz) }; - let _err = baz_fake as &dyn Foo; - //~^ERROR: upcast on a pointer whose vtable does not match its type + let baz_fake: *const dyn Bar = unsafe { std::mem::transmute(baz) }; + let _err = baz_fake as *const dyn Foo; + //~^ERROR: using vtable for trait `Baz` but trait `Bar` was expected } diff --git a/tests/fail/dyn-upcast-trait-mismatch.stderr b/tests/fail/dyn-upcast-trait-mismatch.stderr index 8bac908b86..6a2415cf57 100644 --- a/tests/fail/dyn-upcast-trait-mismatch.stderr +++ b/tests/fail/dyn-upcast-trait-mismatch.stderr @@ -1,8 +1,8 @@ -error: Undefined Behavior: upcast on a pointer whose vtable does not match its type +error: Undefined Behavior: using vtable for trait `Baz` but trait `Bar` was expected --> $DIR/dyn-upcast-trait-mismatch.rs:LL:CC | -LL | let _err = baz_fake as &dyn Foo; - | ^^^^^^^^ upcast on a pointer whose vtable does not match its type +LL | let _err = baz_fake as *const dyn Foo; + | ^^^^^^^^ using vtable for trait `Baz` but trait `Bar` was expected | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/fail/validity/wrong-dyn-trait-generic.rs b/tests/fail/validity/wrong-dyn-trait-generic.rs new file mode 100644 index 0000000000..9b1cefc4b1 --- /dev/null +++ b/tests/fail/validity/wrong-dyn-trait-generic.rs @@ -0,0 +1,12 @@ +use std::mem; + +// Make sure we notice the mismatch also if the difference is "only" in the generic +// parameters of the trait. + +trait Trait {} +impl Trait for T {} + +fn main() { + let x: &dyn Trait = &0; + let _y: *const dyn Trait = unsafe { mem::transmute(x) }; //~ERROR: wrong trait +} diff --git a/tests/fail/validity/wrong-dyn-trait-generic.stderr b/tests/fail/validity/wrong-dyn-trait-generic.stderr new file mode 100644 index 0000000000..1219f9f88c --- /dev/null +++ b/tests/fail/validity/wrong-dyn-trait-generic.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + --> $DIR/wrong-dyn-trait-generic.rs:LL:CC + | +LL | let _y: *const dyn Trait = unsafe { mem::transmute(x) }; + | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `Trait`, but encountered `Trait` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/wrong-dyn-trait-generic.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/fail/validity/wrong-dyn-trait.rs b/tests/fail/validity/wrong-dyn-trait.rs new file mode 100644 index 0000000000..d6049196f2 --- /dev/null +++ b/tests/fail/validity/wrong-dyn-trait.rs @@ -0,0 +1,6 @@ +use std::{fmt, mem}; + +fn main() { + let x: &dyn Send = &0; + let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; //~ERROR: wrong trait +} diff --git a/tests/fail/validity/wrong-dyn-trait.stderr b/tests/fail/validity/wrong-dyn-trait.stderr new file mode 100644 index 0000000000..e3503323b3 --- /dev/null +++ b/tests/fail/validity/wrong-dyn-trait.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` + --> $DIR/wrong-dyn-trait.rs:LL:CC + | +LL | let _y: *const dyn fmt::Debug = unsafe { mem::transmute(x) }; + | ^^^^^^^^^^^^^^^^^ constructing invalid value: wrong trait in wide pointer vtable: expected `std::fmt::Debug`, but encountered `` + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/wrong-dyn-trait.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/pass/cast-rfc0401-vtable-kinds.rs b/tests/pass/cast-rfc0401-vtable-kinds.rs index 2491bda091..adbf5df62c 100644 --- a/tests/pass/cast-rfc0401-vtable-kinds.rs +++ b/tests/pass/cast-rfc0401-vtable-kinds.rs @@ -25,7 +25,7 @@ impl Foo for u32 { impl Bar for () {} unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo + 'a)) -> u32 { - let foo_e: *const dyn Foo = t as *const _; + let foo_e: *const dyn Foo = t as *const _; let r_1 = foo_e as *mut dyn Foo; (&*r_1).foo(0) diff --git a/tests/pass/dyn-upcast.rs b/tests/pass/dyn-upcast.rs index 529b9c471d..ddc4bdcf08 100644 --- a/tests/pass/dyn-upcast.rs +++ b/tests/pass/dyn-upcast.rs @@ -1,24 +1,26 @@ #![feature(trait_upcasting)] #![allow(incomplete_features)] +use std::fmt; + fn main() { basic(); diamond(); struct_(); replace_vptr(); - vtable_mismatch_nop_cast(); + vtable_nop_cast(); } -fn vtable_mismatch_nop_cast() { - let ptr: &dyn std::fmt::Display = &0; - // Even though the vtable is for the wrong trait, this cast doesn't actually change the needed - // vtable so it should still be allowed. - let ptr: *const (dyn std::fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; - let _ptr2 = ptr as *const dyn std::fmt::Debug; +fn vtable_nop_cast() { + let ptr: &dyn fmt::Debug = &0; + // We transmute things around, but the principal trait does not change, so this is allowed. + let ptr: *const (dyn fmt::Debug + Send + Sync) = unsafe { std::mem::transmute(ptr) }; + // This cast is a NOP and should be allowed. + let _ptr2 = ptr as *const dyn fmt::Debug; } fn basic() { - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + trait Foo: PartialEq + fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 } @@ -67,7 +69,7 @@ fn basic() { } let baz: &dyn Baz = &1; - let _: &dyn std::fmt::Debug = baz; + let _: &dyn fmt::Debug = baz; assert_eq!(*baz, 1); assert_eq!(baz.a(), 100); assert_eq!(baz.b(), 200); @@ -77,7 +79,7 @@ fn basic() { assert_eq!(baz.w(), 21); let bar: &dyn Bar = baz; - let _: &dyn std::fmt::Debug = bar; + let _: &dyn fmt::Debug = bar; assert_eq!(*bar, 1); assert_eq!(bar.a(), 100); assert_eq!(bar.b(), 200); @@ -86,14 +88,14 @@ fn basic() { assert_eq!(bar.w(), 21); let foo: &dyn Foo = baz; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); assert_eq!(foo.z(), 11); assert_eq!(foo.y(), 12); let foo: &dyn Foo = bar; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); assert_eq!(foo.z(), 11); @@ -101,7 +103,7 @@ fn basic() { } fn diamond() { - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + trait Foo: PartialEq + fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 } @@ -166,7 +168,7 @@ fn diamond() { } let baz: &dyn Baz = &1; - let _: &dyn std::fmt::Debug = baz; + let _: &dyn fmt::Debug = baz; assert_eq!(*baz, 1); assert_eq!(baz.a(), 100); assert_eq!(baz.b(), 200); @@ -178,7 +180,7 @@ fn diamond() { assert_eq!(baz.v(), 31); let bar1: &dyn Bar1 = baz; - let _: &dyn std::fmt::Debug = bar1; + let _: &dyn fmt::Debug = bar1; assert_eq!(*bar1, 1); assert_eq!(bar1.a(), 100); assert_eq!(bar1.b(), 200); @@ -187,7 +189,7 @@ fn diamond() { assert_eq!(bar1.w(), 21); let bar2: &dyn Bar2 = baz; - let _: &dyn std::fmt::Debug = bar2; + let _: &dyn fmt::Debug = bar2; assert_eq!(*bar2, 1); assert_eq!(bar2.a(), 100); assert_eq!(bar2.c(), 300); @@ -196,17 +198,17 @@ fn diamond() { assert_eq!(bar2.v(), 31); let foo: &dyn Foo = baz; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); let foo: &dyn Foo = bar1; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); let foo: &dyn Foo = bar2; - let _: &dyn std::fmt::Debug = foo; + let _: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); } @@ -215,7 +217,7 @@ fn struct_() { use std::rc::Rc; use std::sync::Arc; - trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + trait Foo: PartialEq + fmt::Debug + Send + Sync { fn a(&self) -> i32 { 10 } From 2d827c953ffbc0369794e60e4ea8269ba5bc1400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Fri, 19 Apr 2024 23:10:04 +0200 Subject: [PATCH 072/208] Add `-Zmiri-env-set` to set environment variables without modifying the host environment This option allows to pass environment variables to the interpreted program without needing to modify the host environment (which may have undesired effects in some cases). --- README.md | 4 ++++ src/bin/miri.rs | 5 +++++ src/eval.rs | 5 ++++- src/shims/env.rs | 34 ++++++++++++++++++++++----------- tests/pass/shims/env/var-set.rs | 7 +++++++ 5 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 tests/pass/shims/env/var-set.rs diff --git a/README.md b/README.md index 948f1ee6c6..fb2ac0b267 100644 --- a/README.md +++ b/README.md @@ -311,6 +311,10 @@ environment variable. We first document the most relevant and most commonly used * `-Zmiri-env-forward=` forwards the `var` environment variable to the interpreted program. Can be used multiple times to forward several variables. Execution will still be deterministic if the value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set. +* `-Zmiri-env-set==` sets the `var` environment variable to `value` in the interpreted. + It can be used to pass environment variables without needing to alter the host environment. It can + be used multiple times to set several variables. If `-Zmiri-disable-isolation` or `-Zmiri-env-forward` + is set, values set with this option will have priority over values from the host environment. * `-Zmiri-ignore-leaks` disables the memory leak checker, and also allows some remaining threads to exist when the main thread exits. * `-Zmiri-isolation-error=` configures Miri's response to operations diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 3f7a965e9d..c3315edac2 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -498,6 +498,11 @@ fn main() { ); } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") { miri_config.forwarded_env_vars.push(param.to_owned()); + } else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") { + let Some((name, value)) = param.split_once('=') else { + show_error!("-Zmiri-env-set requires an argument of the form ="); + }; + miri_config.set_env_vars.insert(name.to_owned(), value.to_owned()); } else if let Some(param) = arg.strip_prefix("-Zmiri-track-pointer-tag=") { let ids: Vec = match parse_comma_list(param) { Ok(ids) => ids, diff --git a/src/eval.rs b/src/eval.rs index df0ede1e1b..3623d97e75 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -9,7 +9,7 @@ use std::thread; use crate::concurrency::thread::TlsAllocAction; use crate::diagnostics::report_leaks; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_middle::ty::{ @@ -100,6 +100,8 @@ pub struct MiriConfig { pub ignore_leaks: bool, /// Environment variables that should always be forwarded from the host. pub forwarded_env_vars: Vec, + /// Additional environment variables that should be set in the interpreted program. + pub set_env_vars: FxHashMap, /// Command-line arguments passed to the interpreted program. pub args: Vec, /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`). @@ -163,6 +165,7 @@ impl Default for MiriConfig { isolated_op: IsolatedOp::Reject(RejectOpWith::Abort), ignore_leaks: false, forwarded_env_vars: vec![], + set_env_vars: FxHashMap::default(), args: vec![], seed: None, tracked_pointer_tags: FxHashSet::default(), diff --git a/src/shims/env.rs b/src/shims/env.rs index 1779189c9c..d97873ce72 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -44,21 +44,15 @@ impl<'tcx> EnvVars<'tcx> { let forward = ecx.machine.communicate() || config.forwarded_env_vars.iter().any(|v| **v == *name); if forward { - let var_ptr = match ecx.tcx.sess.target.os.as_ref() { - _ if ecx.target_os_is_unix() => - alloc_env_var_as_c_str(name.as_ref(), value.as_ref(), ecx)?, - "windows" => alloc_env_var_as_wide_str(name.as_ref(), value.as_ref(), ecx)?, - unsupported => - throw_unsup_format!( - "environment support for target OS `{}` not yet available", - unsupported - ), - }; - ecx.machine.env_vars.map.insert(name.clone(), var_ptr); + add_env_var(ecx, name, value)?; } } } + for (name, value) in &config.set_env_vars { + add_env_var(ecx, OsStr::new(name), OsStr::new(value))?; + } + // Initialize the `environ` pointer when needed. if ecx.target_os_is_unix() { // This is memory backing an extern static, hence `ExternStatic`, not `Env`. @@ -89,6 +83,24 @@ impl<'tcx> EnvVars<'tcx> { } } +fn add_env_var<'mir, 'tcx>( + ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + name: &OsStr, + value: &OsStr, +) -> InterpResult<'tcx, ()> { + let var_ptr = match ecx.tcx.sess.target.os.as_ref() { + _ if ecx.target_os_is_unix() => alloc_env_var_as_c_str(name, value, ecx)?, + "windows" => alloc_env_var_as_wide_str(name, value, ecx)?, + unsupported => + throw_unsup_format!( + "environment support for target OS `{}` not yet available", + unsupported + ), + }; + ecx.machine.env_vars.map.insert(name.to_os_string(), var_ptr); + Ok(()) +} + fn alloc_env_var_as_c_str<'mir, 'tcx>( name: &OsStr, value: &OsStr, diff --git a/tests/pass/shims/env/var-set.rs b/tests/pass/shims/env/var-set.rs new file mode 100644 index 0000000000..2875b6c815 --- /dev/null +++ b/tests/pass/shims/env/var-set.rs @@ -0,0 +1,7 @@ +// Test a value set on the host (MIRI_ENV_VAR_TEST) and one that is not. +//@compile-flags: -Zmiri-env-set=MIRI_ENV_VAR_TEST=test_value_1 -Zmiri-env-set=TEST_VAR_2=test_value_2 + +fn main() { + assert_eq!(std::env::var("MIRI_ENV_VAR_TEST"), Ok("test_value_1".to_owned())); + assert_eq!(std::env::var("TEST_VAR_2"), Ok("test_value_2".to_owned())); +} From 90b408c9d4fdf75dd50e0f2842604898c43351b4 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 22 Apr 2024 06:30:10 +0000 Subject: [PATCH 073/208] Add localtime_r shim --- Cargo.lock | 144 ++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/shims/time.rs | 78 ++++++++++++++++ src/shims/unix/foreign_items.rs | 5 ++ tests/pass-dep/shims/libc-misc.rs | 45 ++++++++++ 5 files changed, 273 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 4fb479e1c5..1e6b5502b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "annotate-snippets" version = "0.9.2" @@ -106,6 +121,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "camino" version = "1.1.6" @@ -150,6 +171,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.3", +] + [[package]] name = "cipher" version = "0.4.4" @@ -216,6 +249,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -319,6 +358,29 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indenter" version = "0.3.3" @@ -372,6 +434,15 @@ dependencies = [ "libc", ] +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -484,6 +555,7 @@ name = "miri" version = "0.1.0" dependencies = [ "aes", + "chrono", "colored", "ctrlc", "getrandom", @@ -512,6 +584,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -964,6 +1045,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + [[package]] name = "winapi" version = "0.3.9" @@ -986,6 +1121,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.3", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 9d24d3c6f4..7748d630b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ smallvec = "1.7" aes = { version = "0.8.3", features = ["hazmat"] } measureme = "11" ctrlc = "3.2.5" +chrono = { version = "0.4.38", default-features = false, features = ["clock"] } # Copied from `compiler/rustc/Cargo.toml`. # But only for some targets, it fails for others. Rustc configures this in its CI, but we can't diff --git a/src/shims/time.rs b/src/shims/time.rs index 1126c90022..dfdf58470d 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -1,5 +1,9 @@ +use std::ffi::OsString; +use std::fmt::Write; use std::time::{Duration, SystemTime}; +use chrono::{DateTime, Datelike, Local, Timelike, Utc}; + use crate::concurrency::thread::MachineCallback; use crate::*; @@ -107,6 +111,80 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(0) } + // The localtime() function shall convert the time in seconds since the Epoch pointed to by + // timer into a broken-down time, expressed as a local time. + // https://linux.die.net/man/3/localtime_r + fn localtime_r( + &mut self, + timep: &OpTy<'tcx, Provenance>, + result_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + + this.assert_target_os_is_unix("localtime_r"); + this.check_no_isolation("`localtime_r`")?; + + let timep = this.deref_pointer(timep)?; + let result = this.deref_pointer_as(result_op, this.libc_ty_layout("tm"))?; + + // The input "represents the number of seconds elapsed since the Epoch, + // 1970-01-01 00:00:00 +0000 (UTC)". + let sec_since_epoch: i64 = this + .read_scalar(&timep)? + .to_int(this.libc_ty_layout("time_t").size)? + .try_into() + .unwrap(); + let dt_utc: DateTime = + DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp"); + // Convert that to local time, then return the broken-down time value. + let dt: DateTime = DateTime::from(dt_utc); + + // This value is always set to -1, because there is no way to know if dst is in effect with + // chrono crate yet. + // This may not be consistent with libc::localtime_r's result. + let tm_isdst = -1; + + // tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08. + // This may not be consistent with libc::localtime_r's result. + let offset_in_second = Local::now().offset().local_minus_utc(); + let tm_gmtoff = offset_in_second; + let mut tm_zone = String::new(); + if offset_in_second < 0 { + tm_zone.push('-'); + } else { + tm_zone.push('+'); + } + let offset_hour = offset_in_second.abs() / 3600; + write!(tm_zone, "{:02}", offset_hour).unwrap(); + let offset_min = (offset_in_second.abs() % 3600) / 60; + if offset_min != 0 { + write!(tm_zone, "{:02}", offset_min).unwrap(); + } + + // FIXME: String de-duplication is needed so that we only allocate this string only once + // even when there are multiple calls to this function. + let tm_zone_ptr = + this.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?; + + this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?; + this.write_int_fields_named( + &[ + ("tm_sec", dt.second().into()), + ("tm_min", dt.minute().into()), + ("tm_hour", dt.hour().into()), + ("tm_mday", dt.day().into()), + ("tm_mon", dt.month0().into()), + ("tm_year", dt.year().checked_sub(1900).unwrap().into()), + ("tm_wday", dt.weekday().num_days_from_sunday().into()), + ("tm_yday", dt.ordinal0().into()), + ("tm_isdst", tm_isdst), + ("tm_gmtoff", tm_gmtoff.into()), + ], + &result, + )?; + + Ok(result.ptr()) + } #[allow(non_snake_case, clippy::arithmetic_side_effects)] fn GetSystemTimeAsFileTime( &mut self, diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index c72d3bb3df..bd299aaa12 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -234,6 +234,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.gettimeofday(tv, tz)?; this.write_scalar(Scalar::from_i32(result), dest)?; } + "localtime_r" => { + let [timep, result_op] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?; + let result = this.localtime_r(timep, result_op)?; + this.write_pointer(result, dest)?; + } "clock_gettime" => { let [clk_id, tp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/tests/pass-dep/shims/libc-misc.rs b/tests/pass-dep/shims/libc-misc.rs index abb384b0a8..f710daf527 100644 --- a/tests/pass-dep/shims/libc-misc.rs +++ b/tests/pass-dep/shims/libc-misc.rs @@ -213,6 +213,50 @@ fn test_posix_gettimeofday() { assert_eq!(is_error, -1); } +fn test_localtime_r() { + use std::ffi::CStr; + use std::{env, ptr}; + + // Set timezone to GMT. + let key = "TZ"; + env::set_var(key, "GMT"); + + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; + let custom_time_ptr = &TIME_SINCE_EPOCH; + let mut tm = libc::tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_gmtoff: 0, + tm_zone: std::ptr::null_mut::(), + }; + let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) }; + + assert_eq!(tm.tm_sec, 56); + assert_eq!(tm.tm_min, 43); + assert_eq!(tm.tm_hour, 7); + assert_eq!(tm.tm_mday, 7); + assert_eq!(tm.tm_mon, 3); + assert_eq!(tm.tm_year, 124); + assert_eq!(tm.tm_wday, 0); + assert_eq!(tm.tm_yday, 97); + assert_eq!(tm.tm_isdst, -1); + assert_eq!(tm.tm_gmtoff, 0); + unsafe { assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") }; + + // The returned value is the pointer passed in. + assert!(ptr::eq(res, &mut tm)); + + //Remove timezone setting. + env::remove_var(key); +} + fn test_isatty() { // Testing whether our isatty shim returns the right value would require controlling whether // these streams are actually TTYs, which is hard. @@ -365,6 +409,7 @@ fn main() { test_posix_realpath_errors(); test_thread_local_errno(); + test_localtime_r(); test_isatty(); From 7a77fdac3d38acd1e99413495deadfea4ae5cf7c Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 21 Apr 2024 18:41:45 +0200 Subject: [PATCH 074/208] Stabilize generic `NonZero`. --- src/bin/miri.rs | 1 - src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/bin/miri.rs b/src/bin/miri.rs index db2cd01ce0..0070d1f3eb 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -1,4 +1,3 @@ -#![feature(generic_nonzero)] #![feature(rustc_private, stmt_expr_attributes)] #![allow( clippy::manual_range_contains, diff --git a/src/lib.rs b/src/lib.rs index 2e19c9ff71..e1c0da9118 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,6 @@ #![feature(cell_update)] #![feature(const_option)] #![feature(float_gamma)] -#![feature(generic_nonzero)] #![feature(map_try_insert)] #![feature(never_type)] #![feature(try_blocks)] From a71761174d8a96cbc7e2c57c48c107f10dcb6c91 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 23 Apr 2024 04:56:35 +0000 Subject: [PATCH 075/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index a60acf44a4..9b0b1c8d23 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -c8d19a92aa9022eb690899cf6d54fd23cb6877e5 +aca749eefceaed0cda19a7ec5e472fce9387bc00 From 506ffc90c00688bd6c953ed953cf708415dc8b00 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 23 Apr 2024 11:17:36 +0200 Subject: [PATCH 076/208] Missing word at the end of sentence --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb2ac0b267..95b99e9a54 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,7 @@ environment variable. We first document the most relevant and most commonly used * `-Zmiri-env-forward=` forwards the `var` environment variable to the interpreted program. Can be used multiple times to forward several variables. Execution will still be deterministic if the value of forwarded variables stays the same. Has no effect if `-Zmiri-disable-isolation` is set. -* `-Zmiri-env-set==` sets the `var` environment variable to `value` in the interpreted. +* `-Zmiri-env-set==` sets the `var` environment variable to `value` in the interpreted program. It can be used to pass environment variables without needing to alter the host environment. It can be used multiple times to set several variables. If `-Zmiri-disable-isolation` or `-Zmiri-env-forward` is set, values set with this option will have priority over values from the host environment. From e1a868f12aef96be42f4da1a08bd9eb46c25fcad Mon Sep 17 00:00:00 2001 From: forcedebug Date: Tue, 23 Apr 2024 19:12:04 +0800 Subject: [PATCH 077/208] chore: fix some typos in comments Signed-off-by: forcedebug --- src/borrow_tracker/stacked_borrows/stack.rs | 2 +- src/shims/windows/foreign_items.rs | 2 +- src/shims/x86/mod.rs | 2 +- src/shims/x86/sse.rs | 2 +- src/shims/x86/sse2.rs | 4 ++-- tests/pass/const-addrs.rs | 2 +- tests/pass/issues/issue-miri-1909.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/borrow_tracker/stacked_borrows/stack.rs b/src/borrow_tracker/stacked_borrows/stack.rs index bebd14d2f1..55ff09c53f 100644 --- a/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/borrow_tracker/stacked_borrows/stack.rs @@ -248,7 +248,7 @@ impl<'tcx> Stack { #[cfg(feature = "stack-cache")] fn find_granting_cache(&mut self, access: AccessKind, tag: BorTag) -> Option { // This looks like a common-sense optimization; we're going to do a linear search of the - // cache or the borrow stack to scan the shorter of the two. This optimization is miniscule + // cache or the borrow stack to scan the shorter of the two. This optimization is minuscule // and this check actually ensures we do not access an invalid cache. // When a stack is created and when items are removed from the top of the borrow stack, we // need some valid value to populate the cache. In both cases, we try to use the bottom diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index ec4c610148..c81d6b2f7f 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -653,7 +653,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { OsStr::new(&formatted), buffer, size.into(), - /*trunacte*/ false, + /*truncate*/ false, )?; if !complete { // The API docs don't say what happens when the buffer is not big enough... diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 615821b2e3..a5bfd30cf6 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -650,7 +650,7 @@ fn convert_float_to_int<'tcx>( let dest = this.project_index(&dest, i)?; let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| { - // Fallback to minimum acording to SSE/AVX semantics. + // Fallback to minimum according to SSE/AVX semantics. ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout) }); this.write_immediate(*res, &dest)?; diff --git a/src/shims/x86/sse.rs b/src/shims/x86/sse.rs index b8c0dfb1c7..1760883731 100644 --- a/src/shims/x86/sse.rs +++ b/src/shims/x86/sse.rs @@ -182,7 +182,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: }; let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| { - // Fallback to minimum acording to SSE semantics. + // Fallback to minimum according to SSE semantics. ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout) }); diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index 9db30d7ddc..9268766fc0 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -420,7 +420,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: }; let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| { - // Fallback to minimum acording to SSE semantics. + // Fallback to minimum according to SSE semantics. ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout) }); @@ -447,7 +447,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let res0 = this.float_to_float_or_int(&right0, dest0.layout)?; this.write_immediate(*res0, &dest0)?; - // Copy remianing from `left` + // Copy remaining from `left` for i in 1..dest_len { this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } diff --git a/tests/pass/const-addrs.rs b/tests/pass/const-addrs.rs index 6c14f0b679..727c67ebfb 100644 --- a/tests/pass/const-addrs.rs +++ b/tests/pass/const-addrs.rs @@ -4,7 +4,7 @@ // deallocated. // In Miri we explicitly store previously-assigned AllocIds for each const and ensure // that we only hand out a finite number of AllocIds per const. -// MIR inlining will put every evaluation of the const we're repeatedly evaluting into the same +// MIR inlining will put every evaluation of the const we're repeatedly evaluating into the same // stack frame, breaking this test. //@compile-flags: -Zinline-mir=no #![feature(strict_provenance)] diff --git a/tests/pass/issues/issue-miri-1909.rs b/tests/pass/issues/issue-miri-1909.rs index ce2114e760..8a2e67cdd0 100644 --- a/tests/pass/issues/issue-miri-1909.rs +++ b/tests/pass/issues/issue-miri-1909.rs @@ -9,7 +9,7 @@ use std::alloc::System; /// `ptr` must be valid for writes of `len` bytes unsafe fn volatile_write_zeroize_mem(ptr: *mut u8, len: usize) { for i in 0..len { - // ptr as usize + i can't overlow because `ptr` is valid for writes of `len` + // ptr as usize + i can't overflow because `ptr` is valid for writes of `len` let ptr_new: *mut u8 = ((ptr as usize) + i) as *mut u8; // SAFETY: `ptr` is valid for writes of `len` bytes, so `ptr_new` is valid for a // byte write From ba15cf3bdca77da2150b70d88debdcc4c955a249 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 14:09:00 +0200 Subject: [PATCH 078/208] add Windows TLS bug to trophy case --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 26f43cd492..ef01ca25fb 100644 --- a/README.md +++ b/README.md @@ -564,7 +564,8 @@ used according to their aliasing restrictions. ## Bugs found by Miri -Miri has already found a number of bugs in the Rust standard library and beyond, which we collect here. +Miri has already found a number of bugs in the Rust standard library and beyond, some of which we collect here. +If Miri helped you find a subtle UB bug in your code, we'd appreciate a PR adding it to the list! Definite bugs found: @@ -599,6 +600,7 @@ Definite bugs found: * [Deallocating with the wrong layout in new specializations for in-place `Iterator::collect`](https://github.com/rust-lang/rust/pull/118460) * [Incorrect offset computation for highly-aligned types in `portable-atomic-util`](https://github.com/taiki-e/portable-atomic/pull/138) * [Occasional memory leak in `std::mpsc` channels](https://github.com/rust-lang/rust/issues/121582) (original code in [crossbeam](https://github.com/crossbeam-rs/crossbeam/pull/1084)) +* [Weak-memory-induced memory leak in Windows thread-local storage](https://github.com/rust-lang/rust/pull/124281) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): From d8e8b892bd8acd2013224a8b3eac2d4ec1d3b9e6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 16:16:57 +0200 Subject: [PATCH 079/208] CI: don't run cron-fail-notify when the job just got canceled Doesn't seem right to prepare a PR in that case --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0dab9f509..4df383d1d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,7 +165,7 @@ jobs: name: cronjob failure notification runs-on: ubuntu-latest needs: [build, style] - if: github.event_name == 'schedule' && (failure() || cancelled()) + if: github.event_name == 'schedule' && failure() steps: # Send a Zulip notification - name: Install zulip-send From 9fd8dd436701e562f0b95f70ea5c8e7c60442dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Fri, 19 Apr 2024 20:46:27 +0200 Subject: [PATCH 080/208] Implement LLVM x86 AVX2 intrinsics --- src/shims/x86/avx.rs | 71 +- src/shims/x86/avx2.rs | 444 ++++++++ src/shims/x86/mod.rs | 405 ++++++++ src/shims/x86/sse2.rs | 75 +- src/shims/x86/sse41.rs | 59 +- src/shims/x86/ssse3.rs | 65 +- tests/pass/intrinsics-x86-avx2.rs | 1613 +++++++++++++++++++++++++++++ 7 files changed, 2476 insertions(+), 256 deletions(-) create mode 100644 src/shims/x86/avx2.rs create mode 100644 tests/pass/intrinsics-x86-avx2.rs diff --git a/src/shims/x86/avx.rs b/src/shims/x86/avx.rs index 23c78647b9..41c20d768f 100644 --- a/src/shims/x86/avx.rs +++ b/src/shims/x86/avx.rs @@ -7,7 +7,8 @@ use rustc_target::spec::abi::Abi; use super::{ bin_op_simd_float_all, conditional_dot_product, convert_float_to_int, horizontal_bin_op, - round_all, test_bits_masked, test_high_bits_masked, unary_op_ps, FloatBinOp, FloatUnaryOp, + mask_load, mask_store, round_all, test_bits_masked, test_high_bits_masked, unary_op_ps, + FloatBinOp, FloatUnaryOp, }; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -347,71 +348,3 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: Ok(EmulateForeignItemResult::NeedsJumping) } } - -/// Conditionally loads from `ptr` according the high bit of each -/// element of `mask`. `ptr` does not need to be aligned. -fn mask_load<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - ptr: &OpTy<'tcx, Provenance>, - mask: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, -) -> InterpResult<'tcx, ()> { - let (mask, mask_len) = this.operand_to_simd(mask)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, mask_len); - - let mask_item_size = mask.layout.field(this, 0).size; - let high_bit_offset = mask_item_size.bits().checked_sub(1).unwrap(); - - let ptr = this.read_pointer(ptr)?; - for i in 0..dest_len { - let mask = this.project_index(&mask, i)?; - let dest = this.project_index(&dest, i)?; - - if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { - // Size * u64 is implemented as always checked - #[allow(clippy::arithmetic_side_effects)] - let ptr = ptr.wrapping_offset(dest.layout.size * i, &this.tcx); - // Unaligned copy, which is what we want. - this.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; - } else { - this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?; - } - } - - Ok(()) -} - -/// Conditionally stores into `ptr` according the high bit of each -/// element of `mask`. `ptr` does not need to be aligned. -fn mask_store<'tcx>( - this: &mut crate::MiriInterpCx<'_, 'tcx>, - ptr: &OpTy<'tcx, Provenance>, - mask: &OpTy<'tcx, Provenance>, - value: &OpTy<'tcx, Provenance>, -) -> InterpResult<'tcx, ()> { - let (mask, mask_len) = this.operand_to_simd(mask)?; - let (value, value_len) = this.operand_to_simd(value)?; - - assert_eq!(value_len, mask_len); - - let mask_item_size = mask.layout.field(this, 0).size; - let high_bit_offset = mask_item_size.bits().checked_sub(1).unwrap(); - - let ptr = this.read_pointer(ptr)?; - for i in 0..value_len { - let mask = this.project_index(&mask, i)?; - let value = this.project_index(&value, i)?; - - if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { - // Size * u64 is implemented as always checked - #[allow(clippy::arithmetic_side_effects)] - let ptr = ptr.wrapping_offset(value.layout.size * i, &this.tcx); - // Unaligned copy, which is what we want. - this.mem_copy(value.ptr(), ptr, value.layout.size, /*nonoverlapping*/ true)?; - } - } - - Ok(()) -} diff --git a/src/shims/x86/avx2.rs b/src/shims/x86/avx2.rs new file mode 100644 index 0000000000..bbf53f9f1e --- /dev/null +++ b/src/shims/x86/avx2.rs @@ -0,0 +1,444 @@ +use crate::rustc_middle::ty::layout::LayoutOf as _; +use rustc_middle::mir; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::spec::abi::Abi; + +use super::{ + horizontal_bin_op, int_abs, mask_load, mask_store, mpsadbw, packssdw, packsswb, packusdw, + packuswb, pmulhrsw, psign, shift_simd_by_scalar, shift_simd_by_simd, ShiftOp, +}; +use crate::*; +use shims::foreign_items::EmulateForeignItemResult; + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: + crate::MiriInterpCxExt<'mir, 'tcx> +{ + fn emulate_x86_avx2_intrinsic( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx, Provenance>], + dest: &MPlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, EmulateForeignItemResult> { + let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, "avx2")?; + // Prefix should have already been checked. + let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.avx2.").unwrap(); + + match unprefixed_name { + // Used to implement the _mm256_abs_epi{8,16,32} functions. + // Calculates the absolute value of packed 8/16/32-bit integers. + "pabs.b" | "pabs.w" | "pabs.d" => { + let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + int_abs(this, op, dest)?; + } + // Used to implement the _mm256_h{add,adds,sub}_epi{16,32} functions. + // Horizontally add / add with saturation / subtract adjacent 16/32-bit + // integer values in `left` and `right`. + "phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (which, saturating) = match unprefixed_name { + "phadd.w" | "phadd.d" => (mir::BinOp::Add, false), + "phadd.sw" => (mir::BinOp::Add, true), + "phsub.w" | "phsub.d" => (mir::BinOp::Sub, false), + "phsub.sw" => (mir::BinOp::Sub, true), + _ => unreachable!(), + }; + + horizontal_bin_op(this, which, saturating, left, right, dest)?; + } + // Used to implement `_mm{,_mask}_{i32,i64}gather_{epi32,epi64,pd,ps}` functions + // Gathers elements from `slice` using `offsets * scale` as indices. + // When the highest bit of the corresponding element of `mask` is 0, + // the value is copied from `src` instead. + "gather.d.d" | "gather.d.d.256" | "gather.d.q" | "gather.d.q.256" | "gather.q.d" + | "gather.q.d.256" | "gather.q.q" | "gather.q.q.256" | "gather.d.pd" + | "gather.d.pd.256" | "gather.q.pd" | "gather.q.pd.256" | "gather.d.ps" + | "gather.d.ps.256" | "gather.q.ps" | "gather.q.ps.256" => { + let [src, slice, offsets, mask, scale] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + assert_eq!(dest.layout, src.layout); + + let (src, _) = this.operand_to_simd(src)?; + let (offsets, offsets_len) = this.operand_to_simd(offsets)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + // There are cases like dest: i32x4, offsets: i64x2 + let actual_len = dest_len.min(offsets_len); + + assert_eq!(dest_len, mask_len); + + let mask_item_size = mask.layout.field(this, 0).size; + let high_bit_offset = mask_item_size.bits().checked_sub(1).unwrap(); + + let scale = this.read_scalar(scale)?.to_i8()?; + if !matches!(scale, 1 | 2 | 4 | 8) { + throw_unsup_format!("invalid gather scale {scale}"); + } + let scale = i64::from(scale); + + let slice = this.read_pointer(slice)?; + for i in 0..actual_len { + let mask = this.project_index(&mask, i)?; + let dest = this.project_index(&dest, i)?; + + if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { + let offset = this.project_index(&offsets, i)?; + let offset = + i64::try_from(this.read_scalar(&offset)?.to_int(offset.layout.size)?) + .unwrap(); + let ptr = slice + .wrapping_signed_offset(offset.checked_mul(scale).unwrap(), &this.tcx); + // Unaligned copy, which is what we want. + this.mem_copy( + ptr, + dest.ptr(), + dest.layout.size, + /*nonoverlapping*/ true, + )?; + } else { + this.copy_op(&this.project_index(&src, i)?, &dest)?; + } + } + for i in actual_len..dest_len { + let dest = this.project_index(&dest, i)?; + this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?; + } + } + // Used to implement the _mm256_madd_epi16 function. + // Multiplies 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`. + "pmadd.wd" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(left_len, right_len); + assert_eq!(dest_len.checked_mul(2).unwrap(), left_len); + + for i in 0..dest_len { + let j1 = i.checked_mul(2).unwrap(); + 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.checked_add(1).unwrap(); + 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).checked_mul(right1.into()).unwrap(); + let mul2 = i32::from(left2).checked_mul(right2.into()).unwrap(); + // 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)?; + } + } + // Used to implement the _mm256_maddubs_epi16 function. + // 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` + // produces the output at index `i`. + "pmadd.ub.sw" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(left_len, right_len); + assert_eq!(dest_len.checked_mul(2).unwrap(), left_len); + + for i in 0..dest_len { + let j1 = i.checked_mul(2).unwrap(); + let left1 = this.read_scalar(&this.project_index(&left, j1)?)?.to_u8()?; + let right1 = this.read_scalar(&this.project_index(&right, j1)?)?.to_i8()?; + + let j2 = j1.checked_add(1).unwrap(); + let left2 = this.read_scalar(&this.project_index(&left, j2)?)?.to_u8()?; + let right2 = this.read_scalar(&this.project_index(&right, j2)?)?.to_i8()?; + + let dest = this.project_index(&dest, i)?; + + // Multiplication of a u8 and an i8 into an i16 cannot overflow. + let mul1 = i16::from(left1).checked_mul(right1.into()).unwrap(); + let mul2 = i16::from(left2).checked_mul(right2.into()).unwrap(); + let res = mul1.saturating_add(mul2); + + this.write_scalar(Scalar::from_i16(res), &dest)?; + } + } + // Used to implement the _mm_maskload_epi32, _mm_maskload_epi64, + // _mm256_maskload_epi32 and _mm256_maskload_epi64 functions. + // For the element `i`, if the high bit of the `i`-th element of `mask` + // is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is + // loaded. + "maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => { + let [ptr, mask] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + mask_load(this, ptr, mask, dest)?; + } + // Used to implement the _mm_maskstore_epi32, _mm_maskstore_epi64, + // _mm256_maskstore_epi32 and _mm256_maskstore_epi64 functions. + // For the element `i`, if the high bit of the element `i`-th of `mask` + // is one, it is stored into `ptr.wapping_add(i)`. + // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores. + "maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => { + let [ptr, mask, value] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + mask_store(this, ptr, mask, value)?; + } + // Used to implement the _mm256_mpsadbw_epu8 function. + // Compute the sum of absolute differences of quadruplets of unsigned + // 8-bit integers in `left` and `right`, and store the 16-bit results + // in `right`. Quadruplets are selected from `left` and `right` with + // offsets specified in `imm`. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8 + "mpsadbw" => { + let [left, right, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + mpsadbw(this, left, right, imm, dest)?; + } + // Used to implement the _mm256_mulhrs_epi16 function. + // Multiplies packed 16-bit signed integer values, truncates the 32-bit + // product to the 18 most significant bits by right-shifting, and then + // divides the 18-bit value by 2 (rounding to nearest) by first adding + // 1 and then taking the bits `1..=16`. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16 + "pmul.hr.sw" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + pmulhrsw(this, left, right, dest)?; + } + // Used to implement the _mm256_packs_epi16 function. + // Converts two 16-bit integer vectors to a single 8-bit integer + // vector with signed saturation. + "packsswb" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + packsswb(this, left, right, dest)?; + } + // Used to implement the _mm256_packs_epi32 function. + // Converts two 32-bit integer vectors to a single 16-bit integer + // vector with signed saturation. + "packssdw" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + packssdw(this, left, right, dest)?; + } + // Used to implement the _mm256_packus_epi16 function. + // Converts two 16-bit signed integer vectors to a single 8-bit + // unsigned integer vector with saturation. + "packuswb" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + packuswb(this, left, right, dest)?; + } + // Used to implement the _mm256_packus_epi32 function. + // Concatenates two 32-bit signed integer vectors and converts + // the result to a 16-bit unsigned integer vector with saturation. + "packusdw" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + packusdw(this, left, right, dest)?; + } + // Used to implement the _mm256_permutevar8x32_epi32 and + // _mm256_permutevar8x32_ps function. + // Shuffles `left` using the three low bits of each element of `right` + // as indices. + "permd" | "permps" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let dest = this.project_index(&dest, i)?; + let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u32()?; + let left = this.project_index(&left, (right & 0b111).into())?; + + this.copy_op(&left, &dest)?; + } + } + // Used to implement the _mm256_permute2x128_si256 function. + // Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern. + "vperm2i128" => { + let [left, right, imm] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + assert_eq!(left.layout.size.bits(), 256); + assert_eq!(right.layout.size.bits(), 256); + assert_eq!(dest.layout.size.bits(), 256); + + // Transmute to `[i128; 2]` + + let array_layout = + this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.i128, 2))?; + let left = left.transmute(array_layout, this)?; + let right = right.transmute(array_layout, this)?; + let dest = dest.transmute(array_layout, this)?; + + let imm = this.read_scalar(imm)?.to_u8()?; + + for i in 0..2 { + let dest = this.project_index(&dest, i)?; + let src = match (imm >> i.checked_mul(4).unwrap()) & 0b11 { + 0 => this.project_index(&left, 0)?, + 1 => this.project_index(&left, 1)?, + 2 => this.project_index(&right, 0)?, + 3 => this.project_index(&right, 1)?, + _ => unreachable!(), + }; + + this.copy_op(&src, &dest)?; + } + } + // Used to implement the _mm256_sad_epu8 function. + // Compute the absolute differences of packed unsigned 8-bit integers + // in `left` and `right`, then horizontally sum each consecutive 8 + // differences to produce four unsigned 16-bit integers, and pack + // these unsigned 16-bit integers in the low 16 bits of 64-bit elements + // in `dest`. + // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8 + "psad.bw" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(left_len, right_len); + assert_eq!(left_len, dest_len.checked_mul(8).unwrap()); + + for i in 0..dest_len { + let dest = this.project_index(&dest, i)?; + + let mut acc: u16 = 0; + for j in 0..8 { + let src_index = i.checked_mul(8).unwrap().checked_add(j).unwrap(); + + let left = this.project_index(&left, src_index)?; + let left = this.read_scalar(&left)?.to_u8()?; + + let right = this.project_index(&right, src_index)?; + let right = this.read_scalar(&right)?.to_u8()?; + + acc = acc.checked_add(left.abs_diff(right).into()).unwrap(); + } + + this.write_scalar(Scalar::from_u64(acc.into()), &dest)?; + } + } + // Used to implement the _mm256_shuffle_epi8 intrinsic. + // Shuffles bytes from `left` using `right` as pattern. + // Each 128-bit block is shuffled independently. + "pshuf.b" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?; + let dest = this.project_index(&dest, i)?; + + let res = if right & 0x80 == 0 { + // Shuffle each 128-bit (16-byte) block independently. + let j = u64::from(right % 16).checked_add(i & !15).unwrap(); + this.read_scalar(&this.project_index(&left, j)?)? + } else { + // If the highest bit in `right` is 1, write zero. + Scalar::from_u8(0) + }; + + this.write_scalar(res, &dest)?; + } + } + // Used to implement the _mm256_sign_epi{8,16,32} functions. + // Negates elements from `left` when the corresponding element in + // `right` is negative. If an element from `right` is zero, zero + // is writen to the corresponding output element. + // Basically, we multiply `left` with `right.signum()`. + "psign.b" | "psign.w" | "psign.d" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + psign(this, left, right, dest)?; + } + // Used to implement the _mm256_{sll,srl,sra}_epi{16,32,64} functions + // (except _mm256_sra_epi64, which is not available in AVX2). + // Shifts N-bit packed integers in left by the amount in right. + // `right` is as 128-bit vector. but it is interpreted as a single + // 64-bit integer (remaining bits are ignored). + // For logic shifts, when right is larger than N - 1, zero is produced. + // For arithmetic shifts, when right is larger than N - 1, the sign bit + // is copied to remaining bits. + "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" + | "psrl.q" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let which = match unprefixed_name { + "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, + "psrl.w" | "psrl.d" | "psrl.q" => ShiftOp::RightLogic, + "psra.w" | "psra.d" => ShiftOp::RightArith, + _ => unreachable!(), + }; + + shift_simd_by_scalar(this, left, right, which, dest)?; + } + // Used to implement the _mm{,256}_{sllv,srlv,srav}_epi{32,64} functions + // (except _mm{,256}_srav_epi64, which are not available in AVX2). + "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256" + | "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => { + let [left, right] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + + let which = match unprefixed_name { + "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left, + "psrlv.d" | "psrlv.d.256" | "psrlv.q" | "psrlv.q.256" => ShiftOp::RightLogic, + "psrav.d" | "psrav.d.256" => ShiftOp::RightArith, + _ => unreachable!(), + }; + + shift_simd_by_simd(this, left, right, which, dest)?; + } + _ => return Ok(EmulateForeignItemResult::NotSupported), + } + Ok(EmulateForeignItemResult::NeedsJumping) + } +} diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 615821b2e3..a9d248c2a8 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -14,6 +14,7 @@ use shims::foreign_items::EmulateForeignItemResult; mod aesni; mod avx; +mod avx2; mod sse; mod sse2; mod sse3; @@ -136,6 +137,11 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this, link_name, abi, args, dest, ); } + name if name.starts_with("avx2.") => { + return avx2::EvalContextExt::emulate_x86_avx2_intrinsic( + this, link_name, abi, args, dest, + ); + } _ => return Ok(EmulateForeignItemResult::NotSupported), } @@ -534,6 +540,61 @@ fn shift_simd_by_scalar<'tcx>( Ok(()) } +/// Shifts each element of `left` by the corresponding element of `right`. +/// +/// For logic shifts, when right is larger than BITS - 1, zero is produced. +/// For arithmetic right-shifts, when right is larger than BITS - 1, the sign +/// bit is copied to remaining bits. +fn shift_simd_by_simd<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + which: ShiftOp, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let left = this.read_scalar(&this.project_index(&left, i)?)?; + let right = this.read_scalar(&this.project_index(&right, i)?)?; + let dest = this.project_index(&dest, i)?; + + // It is ok to saturate the value to u32::MAX because any value + // above BITS - 1 will produce the same result. + let shift = u32::try_from(right.to_uint(dest.layout.size)?).unwrap_or(u32::MAX); + + let res = match which { + ShiftOp::Left => { + let left = left.to_uint(dest.layout.size)?; + let res = left.checked_shl(shift).unwrap_or(0); + // `truncate` is needed as left-shift can make the absolute value larger. + Scalar::from_uint(dest.layout.size.truncate(res), dest.layout.size) + } + ShiftOp::RightLogic => { + let left = left.to_uint(dest.layout.size)?; + let res = left.checked_shr(shift).unwrap_or(0); + // No `truncate` needed as right-shift can only make the absolute value smaller. + Scalar::from_uint(res, dest.layout.size) + } + ShiftOp::RightArith => { + let left = left.to_int(dest.layout.size)?; + // On overflow, copy the sign bit to the remaining bits + let res = left.checked_shr(shift).unwrap_or(left >> 127); + // No `truncate` needed as right-shift can only make the absolute value smaller. + Scalar::from_int(res, dest.layout.size) + } + }; + this.write_scalar(res, &dest)?; + } + + Ok(()) +} + /// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts /// the first value. fn extract_first_u64<'tcx>( @@ -664,6 +725,33 @@ fn convert_float_to_int<'tcx>( Ok(()) } +/// Calculates absolute value of integers in `op` and stores the result in `dest`. +/// +/// In case of overflow (when the operand is the minimum value), the operation +/// will wrap around. +fn int_abs<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + op: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (op, op_len) = this.operand_to_simd(op)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(op_len, dest_len); + + for i in 0..dest_len { + let op = this.read_scalar(&this.project_index(&op, i)?)?; + let dest = this.project_index(&dest, i)?; + + // Converting to a host "i128" works since the input is always signed. + let res = op.to_int(dest.layout.size)?.unsigned_abs(); + + this.write_scalar(Scalar::from_uint(res, dest.layout.size), &dest)?; + } + + Ok(()) +} + /// Splits `op` (which must be a SIMD vector) into 128-bit chuncks. /// /// Returns a tuple where: @@ -874,3 +962,320 @@ fn test_high_bits_masked<'tcx>( Ok((direct, negated)) } + +/// Conditionally loads from `ptr` according the high bit of each +/// element of `mask`. `ptr` does not need to be aligned. +fn mask_load<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + ptr: &OpTy<'tcx, Provenance>, + mask: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (mask, mask_len) = this.operand_to_simd(mask)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, mask_len); + + let mask_item_size = mask.layout.field(this, 0).size; + let high_bit_offset = mask_item_size.bits().checked_sub(1).unwrap(); + + let ptr = this.read_pointer(ptr)?; + for i in 0..dest_len { + let mask = this.project_index(&mask, i)?; + let dest = this.project_index(&dest, i)?; + + if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { + // Size * u64 is implemented as always checked + #[allow(clippy::arithmetic_side_effects)] + let ptr = ptr.wrapping_offset(dest.layout.size * i, &this.tcx); + // Unaligned copy, which is what we want. + this.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; + } else { + this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?; + } + } + + Ok(()) +} + +/// Conditionally stores into `ptr` according the high bit of each +/// element of `mask`. `ptr` does not need to be aligned. +fn mask_store<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + ptr: &OpTy<'tcx, Provenance>, + mask: &OpTy<'tcx, Provenance>, + value: &OpTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (mask, mask_len) = this.operand_to_simd(mask)?; + let (value, value_len) = this.operand_to_simd(value)?; + + assert_eq!(value_len, mask_len); + + let mask_item_size = mask.layout.field(this, 0).size; + let high_bit_offset = mask_item_size.bits().checked_sub(1).unwrap(); + + let ptr = this.read_pointer(ptr)?; + for i in 0..value_len { + let mask = this.project_index(&mask, i)?; + let value = this.project_index(&value, i)?; + + if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { + // Size * u64 is implemented as always checked + #[allow(clippy::arithmetic_side_effects)] + let ptr = ptr.wrapping_offset(value.layout.size * i, &this.tcx); + // Unaligned copy, which is what we want. + this.mem_copy(value.ptr(), ptr, value.layout.size, /*nonoverlapping*/ true)?; + } + } + + Ok(()) +} + +/// Compute the sum of absolute differences of quadruplets of unsigned +/// 8-bit integers in `left` and `right`, and store the 16-bit results +/// in `right`. Quadruplets are selected from `left` and `right` with +/// offsets specified in `imm`. +/// +/// +/// +/// +/// Each 128-bit chunk is treated independently (i.e., the value for +/// the is i-th 128-bit chunk of `dest` is calculated with the i-th +/// 128-bit chunks of `left` and `right`). +fn mpsadbw<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + imm: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + assert_eq!(left.layout, right.layout); + assert_eq!(left.layout.size, dest.layout.size); + + let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?; + let (_, _, right) = split_simd_to_128bit_chunks(this, right)?; + let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(this, dest)?; + + assert_eq!(op_items_per_chunk, dest_items_per_chunk.checked_mul(2).unwrap()); + + let imm = this.read_scalar(imm)?.to_uint(imm.layout.size)?; + // Bit 2 of `imm` specifies the offset for indices of `left`. + // The offset is 0 when the bit is 0 or 4 when the bit is 1. + let left_offset = u64::try_from((imm >> 2) & 1).unwrap().checked_mul(4).unwrap(); + // Bits 0..=1 of `imm` specify the offset for indices of + // `right` in blocks of 4 elements. + let right_offset = u64::try_from(imm & 0b11).unwrap().checked_mul(4).unwrap(); + + for i in 0..num_chunks { + let left = this.project_index(&left, i)?; + let right = this.project_index(&right, i)?; + let dest = this.project_index(&dest, i)?; + + for j in 0..dest_items_per_chunk { + let left_offset = left_offset.checked_add(j).unwrap(); + let mut res: u16 = 0; + for k in 0..4 { + let left = this + .read_scalar(&this.project_index(&left, left_offset.checked_add(k).unwrap())?)? + .to_u8()?; + let right = this + .read_scalar( + &this.project_index(&right, right_offset.checked_add(k).unwrap())?, + )? + .to_u8()?; + res = res.checked_add(left.abs_diff(right).into()).unwrap(); + } + this.write_scalar(Scalar::from_u16(res), &this.project_index(&dest, j)?)?; + } + } + + Ok(()) +} + +/// Multiplies packed 16-bit signed integer values, truncates the 32-bit +/// product to the 18 most significant bits by right-shifting, and then +/// divides the 18-bit value by 2 (rounding to nearest) by first adding +/// 1 and then taking the bits `1..=16`. +/// +/// +/// +fn pmulhrsw<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?; + let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?; + let dest = this.project_index(&dest, i)?; + + let res = + (i32::from(left).checked_mul(right.into()).unwrap() >> 14).checked_add(1).unwrap() >> 1; + + // The result of this operation can overflow a signed 16-bit integer. + // When `left` and `right` are -0x8000, the result is 0x8000. + #[allow(clippy::cast_possible_truncation)] + let res = res as i16; + + this.write_scalar(Scalar::from_i16(res), &dest)?; + } + + Ok(()) +} + +fn pack_generic<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, + f: impl Fn(Scalar) -> InterpResult<'tcx, Scalar>, +) -> InterpResult<'tcx, ()> { + assert_eq!(left.layout, right.layout); + assert_eq!(left.layout.size, dest.layout.size); + + let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?; + let (_, _, right) = split_simd_to_128bit_chunks(this, right)?; + let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(this, dest)?; + + assert_eq!(dest_items_per_chunk, op_items_per_chunk.checked_mul(2).unwrap()); + + for i in 0..num_chunks { + let left = this.project_index(&left, i)?; + let right = this.project_index(&right, i)?; + let dest = this.project_index(&dest, i)?; + + for j in 0..op_items_per_chunk { + let left = this.read_scalar(&this.project_index(&left, j)?)?; + let right = this.read_scalar(&this.project_index(&right, j)?)?; + let left_dest = this.project_index(&dest, j)?; + let right_dest = + this.project_index(&dest, j.checked_add(op_items_per_chunk).unwrap())?; + + let left_res = f(left)?; + let right_res = f(right)?; + + this.write_scalar(left_res, &left_dest)?; + this.write_scalar(right_res, &right_dest)?; + } + } + + Ok(()) +} + +/// Converts two 16-bit integer vectors to a single 8-bit integer +/// vector with signed saturation. +/// +/// Each 128-bit chunk is treated independently (i.e., the value for +/// the is i-th 128-bit chunk of `dest` is calculated with the i-th +/// 128-bit chunks of `left` and `right`). +fn packsswb<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + pack_generic(this, left, right, dest, |op| { + let op = op.to_i16()?; + let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX }); + Ok(Scalar::from_i8(res)) + }) +} + +/// Converts two 16-bit signed integer vectors to a single 8-bit +/// unsigned integer vector with saturation. +/// +/// Each 128-bit chunk is treated independently (i.e., the value for +/// the is i-th 128-bit chunk of `dest` is calculated with the i-th +/// 128-bit chunks of `left` and `right`). +fn packuswb<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + pack_generic(this, left, right, dest, |op| { + let op = op.to_i16()?; + let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX }); + Ok(Scalar::from_u8(res)) + }) +} + +/// Converts two 32-bit integer vectors to a single 16-bit integer +/// vector with signed saturation. +/// +/// Each 128-bit chunk is treated independently (i.e., the value for +/// the is i-th 128-bit chunk of `dest` is calculated with the i-th +/// 128-bit chunks of `left` and `right`). +fn packssdw<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + pack_generic(this, left, right, dest, |op| { + let op = op.to_i32()?; + let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX }); + Ok(Scalar::from_i16(res)) + }) +} + +/// Converts two 32-bit integer vectors to a single 16-bit integer +/// vector with unsigned saturation. +/// +/// Each 128-bit chunk is treated independently (i.e., the value for +/// the is i-th 128-bit chunk of `dest` is calculated with the i-th +/// 128-bit chunks of `left` and `right`). +fn packusdw<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + pack_generic(this, left, right, dest, |op| { + let op = op.to_i32()?; + let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX }); + Ok(Scalar::from_u16(res)) + }) +} + +/// Negates elements from `left` when the corresponding element in +/// `right` is negative. If an element from `right` is zero, zero +/// is writen to the corresponding output element. +/// In other words, multiplies `left` with `right.signum()`. +fn psign<'tcx>( + this: &mut crate::MiriInterpCx<'_, 'tcx>, + left: &OpTy<'tcx, Provenance>, + right: &OpTy<'tcx, Provenance>, + dest: &MPlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = this.operand_to_simd(left)?; + let (right, right_len) = this.operand_to_simd(right)?; + let (dest, dest_len) = this.mplace_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let dest = this.project_index(&dest, i)?; + let left = this.read_immediate(&this.project_index(&left, i)?)?; + let right = this.read_scalar(&this.project_index(&right, i)?)?.to_int(dest.layout.size)?; + + let res = this.wrapping_binary_op( + mir::BinOp::Mul, + &left, + &ImmTy::from_int(right.signum(), dest.layout), + )?; + + this.write_immediate(*res, &dest)?; + } + + Ok(()) +} diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index 9db30d7ddc..63b6a30194 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -3,8 +3,8 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use super::{ - bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, shift_simd_by_scalar, - FloatBinOp, ShiftOp, + bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, packssdw, packsswb, + packuswb, shift_simd_by_scalar, FloatBinOp, ShiftOp, }; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -176,29 +176,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - // left and right are i16x8, dest is i8x16 - assert_eq!(left_len, 8); - assert_eq!(right_len, 8); - assert_eq!(dest_len, 16); - - for i in 0..left_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?; - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?; - let left_dest = this.project_index(&dest, i)?; - let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?; - - let left_res = - i8::try_from(left).unwrap_or(if left < 0 { i8::MIN } else { i8::MAX }); - let right_res = - i8::try_from(right).unwrap_or(if right < 0 { i8::MIN } else { i8::MAX }); - - this.write_scalar(Scalar::from_i8(left_res), &left_dest)?; - this.write_scalar(Scalar::from_i8(right_res), &right_dest)?; - } + packsswb(this, left, right, dest)?; } // Used to implement the _mm_packus_epi16 function. // Converts two 16-bit signed integer vectors to a single 8-bit @@ -207,28 +185,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - // left and right are i16x8, dest is u8x16 - assert_eq!(left_len, 8); - assert_eq!(right_len, 8); - assert_eq!(dest_len, 16); - - for i in 0..left_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?; - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?; - let left_dest = this.project_index(&dest, i)?; - let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?; - - let left_res = u8::try_from(left).unwrap_or(if left < 0 { 0 } else { u8::MAX }); - let right_res = - u8::try_from(right).unwrap_or(if right < 0 { 0 } else { u8::MAX }); - - this.write_scalar(Scalar::from_u8(left_res), &left_dest)?; - this.write_scalar(Scalar::from_u8(right_res), &right_dest)?; - } + packuswb(this, left, right, dest)?; } // Used to implement the _mm_packs_epi32 function. // Converts two 32-bit integer vectors to a single 16-bit integer @@ -237,29 +194,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - // left and right are i32x4, dest is i16x8 - assert_eq!(left_len, 4); - assert_eq!(right_len, 4); - assert_eq!(dest_len, 8); - - for i in 0..left_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i32()?; - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i32()?; - let left_dest = this.project_index(&dest, i)?; - let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?; - - let left_res = - i16::try_from(left).unwrap_or(if left < 0 { i16::MIN } else { i16::MAX }); - let right_res = - i16::try_from(right).unwrap_or(if right < 0 { i16::MIN } else { i16::MAX }); - - this.write_scalar(Scalar::from_i16(left_res), &left_dest)?; - this.write_scalar(Scalar::from_i16(right_res), &right_dest)?; - } + packssdw(this, left, right, dest)?; } // Used to implement _mm_min_sd and _mm_max_sd functions. // Note that the semantics are a bit different from Rust simd_min diff --git a/src/shims/x86/sse41.rs b/src/shims/x86/sse41.rs index 16a82eed99..19bc27421d 100644 --- a/src/shims/x86/sse41.rs +++ b/src/shims/x86/sse41.rs @@ -1,7 +1,7 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; -use super::{conditional_dot_product, round_all, round_first, test_bits_masked}; +use super::{conditional_dot_product, mpsadbw, packusdw, round_all, round_first, test_bits_masked}; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -68,27 +68,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(left_len, right_len); - assert_eq!(dest_len, left_len.checked_mul(2).unwrap()); - - for i in 0..left_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i32()?; - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i32()?; - let left_dest = this.project_index(&dest, i)?; - let right_dest = this.project_index(&dest, i.checked_add(left_len).unwrap())?; - - let left_res = - u16::try_from(left).unwrap_or(if left < 0 { 0 } else { u16::MAX }); - let right_res = - u16::try_from(right).unwrap_or(if right < 0 { 0 } else { u16::MAX }); - - this.write_scalar(Scalar::from_u16(left_res), &left_dest)?; - this.write_scalar(Scalar::from_u16(right_res), &right_dest)?; - } + packusdw(this, left, right, dest)?; } // Used to implement the _mm_dp_ps and _mm_dp_pd functions. // Conditionally multiplies the packed floating-point elements in @@ -176,40 +156,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(left_len, right_len); - assert_eq!(left_len, dest_len.checked_mul(2).unwrap()); - - let imm = this.read_scalar(imm)?.to_u8()?; - // Bit 2 of `imm` specifies the offset for indices of `left`. - // The offset is 0 when the bit is 0 or 4 when the bit is 1. - let left_offset = u64::from((imm >> 2) & 1).checked_mul(4).unwrap(); - // Bits 0..=1 of `imm` specify the offset for indices of - // `right` in blocks of 4 elements. - let right_offset = u64::from(imm & 0b11).checked_mul(4).unwrap(); - - for i in 0..dest_len { - let left_offset = left_offset.checked_add(i).unwrap(); - let mut res: u16 = 0; - for j in 0..4 { - let left = this - .read_scalar( - &this.project_index(&left, left_offset.checked_add(j).unwrap())?, - )? - .to_u8()?; - let right = this - .read_scalar( - &this - .project_index(&right, right_offset.checked_add(j).unwrap())?, - )? - .to_u8()?; - res = res.checked_add(left.abs_diff(right).into()).unwrap(); - } - this.write_scalar(Scalar::from_u16(res), &this.project_index(&dest, i)?)?; - } + mpsadbw(this, left, right, imm, dest)?; } // Used to implement the _mm_testz_si128, _mm_testc_si128 // and _mm_testnzc_si128 functions. diff --git a/src/shims/x86/ssse3.rs b/src/shims/x86/ssse3.rs index dd5d064b20..4f8e52dbb7 100644 --- a/src/shims/x86/ssse3.rs +++ b/src/shims/x86/ssse3.rs @@ -2,7 +2,7 @@ use rustc_middle::mir; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; -use super::horizontal_bin_op; +use super::{horizontal_bin_op, int_abs, pmulhrsw, psign}; use crate::*; use shims::foreign_items::EmulateForeignItemResult; @@ -28,20 +28,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: "pabs.b.128" | "pabs.w.128" | "pabs.d.128" => { let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (op, op_len) = this.operand_to_simd(op)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(op_len, dest_len); - - for i in 0..dest_len { - let op = this.read_scalar(&this.project_index(&op, i)?)?; - let dest = this.project_index(&dest, i)?; - - // Converting to a host "i128" works since the input is always signed. - let res = op.to_int(dest.layout.size)?.unsigned_abs(); - - this.write_scalar(Scalar::from_uint(res, dest.layout.size), &dest)?; - } + int_abs(this, op, dest)?; } // Used to implement the _mm_shuffle_epi8 intrinsic. // Shuffles bytes from `left` using `right` as pattern. @@ -136,30 +123,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - for i in 0..dest_len { - let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?; - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?; - let dest = this.project_index(&dest, i)?; - - let res = (i32::from(left).checked_mul(right.into()).unwrap() >> 14) - .checked_add(1) - .unwrap() - >> 1; - - // The result of this operation can overflow a signed 16-bit integer. - // When `left` and `right` are -0x8000, the result is 0x8000. - #[allow(clippy::cast_possible_truncation)] - let res = res as i16; - - this.write_scalar(Scalar::from_i16(res), &dest)?; - } + pmulhrsw(this, left, right, dest)?; } // Used to implement the _mm_sign_epi{8,16,32} functions. // Negates elements from `left` when the corresponding element in @@ -170,28 +134,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let (left, left_len) = this.operand_to_simd(left)?; - let (right, right_len) = this.operand_to_simd(right)?; - let (dest, dest_len) = this.mplace_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - for i in 0..dest_len { - let dest = this.project_index(&dest, i)?; - let left = this.read_immediate(&this.project_index(&left, i)?)?; - let right = this - .read_scalar(&this.project_index(&right, i)?)? - .to_int(dest.layout.size)?; - - let res = this.wrapping_binary_op( - mir::BinOp::Mul, - &left, - &ImmTy::from_int(right.signum(), dest.layout), - )?; - - this.write_immediate(*res, &dest)?; - } + psign(this, left, right, dest)?; } _ => return Ok(EmulateForeignItemResult::NotSupported), } diff --git a/tests/pass/intrinsics-x86-avx2.rs b/tests/pass/intrinsics-x86-avx2.rs new file mode 100644 index 0000000000..80d125bb85 --- /dev/null +++ b/tests/pass/intrinsics-x86-avx2.rs @@ -0,0 +1,1613 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=+avx2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::mem::transmute; + +fn main() { + assert!(is_x86_feature_detected!("avx2")); + + unsafe { + test_avx2(); + } +} + +#[target_feature(enable = "avx2")] +unsafe fn test_avx2() { + // Mostly copied from library/stdarch/crates/core_arch/src/x86/avx2.rs + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_abs_epi32() { + #[rustfmt::skip] + let a = _mm256_setr_epi32( + 0, 1, -1, i32::MAX, + i32::MIN, 100, -100, -32, + ); + let r = _mm256_abs_epi32(a); + #[rustfmt::skip] + let e = _mm256_setr_epi32( + 0, 1, 1, i32::MAX, + i32::MAX.wrapping_add(1), 100, 100, 32, + ); + assert_eq_m256i(r, e); + } + test_mm256_abs_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_abs_epi16() { + #[rustfmt::skip] + let a = _mm256_setr_epi16( + 0, 1, -1, 2, -2, 3, -3, 4, + -4, 5, -5, i16::MAX, i16::MIN, 100, -100, -32, + ); + let r = _mm256_abs_epi16(a); + #[rustfmt::skip] + let e = _mm256_setr_epi16( + 0, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, i16::MAX, i16::MAX.wrapping_add(1), 100, 100, 32, + ); + assert_eq_m256i(r, e); + } + test_mm256_abs_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_abs_epi8() { + #[rustfmt::skip] + let a = _mm256_setr_epi8( + 0, 1, -1, 2, -2, 3, -3, 4, + -4, 5, -5, i8::MAX, i8::MIN, 100, -100, -32, + 0, 1, -1, 2, -2, 3, -3, 4, + -4, 5, -5, i8::MAX, i8::MIN, 100, -100, -32, + ); + let r = _mm256_abs_epi8(a); + #[rustfmt::skip] + let e = _mm256_setr_epi8( + 0, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, i8::MAX, i8::MAX.wrapping_add(1), 100, 100, 32, + 0, 1, 1, 2, 2, 3, 3, 4, + 4, 5, 5, i8::MAX, i8::MAX.wrapping_add(1), 100, 100, 32, + ); + assert_eq_m256i(r, e); + } + test_mm256_abs_epi8(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_hadd_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(4); + let r = _mm256_hadd_epi16(a, b); + let e = _mm256_setr_epi16(4, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8); + assert_eq_m256i(r, e); + + // Test wrapping on overflow + let a = _mm256_setr_epi16( + i16::MAX, + 1, + i16::MAX, + 2, + i16::MAX, + 3, + i16::MAX, + 4, + i16::MAX, + 5, + i16::MAX, + 6, + i16::MAX, + 7, + i16::MAX, + 8, + ); + let b = _mm256_setr_epi16( + i16::MIN, + -1, + i16::MIN, + -2, + i16::MIN, + -3, + i16::MIN, + -4, + i16::MIN, + -5, + i16::MIN, + -6, + i16::MIN, + -7, + i16::MIN, + -8, + ); + let expected = _mm256_setr_epi16( + i16::MIN, + i16::MIN + 1, + i16::MIN + 2, + i16::MIN + 3, + i16::MAX, + i16::MAX - 1, + i16::MAX - 2, + i16::MAX - 3, + i16::MIN + 4, + i16::MIN + 5, + i16::MIN + 6, + i16::MIN + 7, + i16::MAX - 4, + i16::MAX - 5, + i16::MAX - 6, + i16::MAX - 7, + ); + let r = _mm256_hadd_epi16(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_hadd_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_hadd_epi32() { + let a = _mm256_set1_epi32(2); + let b = _mm256_set1_epi32(4); + let r = _mm256_hadd_epi32(a, b); + let e = _mm256_setr_epi32(4, 4, 8, 8, 4, 4, 8, 8); + assert_eq_m256i(r, e); + + // Test wrapping on overflow + let a = _mm256_setr_epi32(i32::MAX, 1, i32::MAX, 2, i32::MAX, 3, i32::MAX, 4); + let b = _mm256_setr_epi32(i32::MIN, -1, i32::MIN, -2, i32::MIN, -3, i32::MIN, -4); + let expected = _mm256_setr_epi32( + i32::MIN, + i32::MIN + 1, + i32::MAX, + i32::MAX - 1, + i32::MIN + 2, + i32::MIN + 3, + i32::MAX - 2, + i32::MAX - 3, + ); + let r = _mm256_hadd_epi32(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_hadd_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_hadds_epi16() { + let a = _mm256_set1_epi16(2); + let a = _mm256_insert_epi16::<0>(a, 0x7fff); + let a = _mm256_insert_epi16::<1>(a, 1); + let b = _mm256_set1_epi16(4); + let r = _mm256_hadds_epi16(a, b); + let e = _mm256_setr_epi16(0x7FFF, 4, 4, 4, 8, 8, 8, 8, 4, 4, 4, 4, 8, 8, 8, 8); + assert_eq_m256i(r, e); + + // Test saturating on overflow + let a = _mm256_setr_epi16( + i16::MAX, + 1, + i16::MAX, + 2, + i16::MAX, + 3, + i16::MAX, + 4, + i16::MAX, + 5, + i16::MAX, + 6, + i16::MAX, + 7, + i16::MAX, + 8, + ); + let b = _mm256_setr_epi16( + i16::MIN, + -1, + i16::MIN, + -2, + i16::MIN, + -3, + i16::MIN, + -4, + i16::MIN, + -5, + i16::MIN, + -6, + i16::MIN, + -7, + i16::MIN, + -8, + ); + let expected = _mm256_setr_epi16( + i16::MAX, + i16::MAX, + i16::MAX, + i16::MAX, + i16::MIN, + i16::MIN, + i16::MIN, + i16::MIN, + i16::MAX, + i16::MAX, + i16::MAX, + i16::MAX, + i16::MIN, + i16::MIN, + i16::MIN, + i16::MIN, + ); + let r = _mm256_hadds_epi16(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_hadds_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_hsub_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(4); + let r = _mm256_hsub_epi16(a, b); + let e = _mm256_set1_epi16(0); + assert_eq_m256i(r, e); + + // Test wrapping on overflow + let a = _mm256_setr_epi16( + i16::MAX, + -1, + i16::MAX, + -2, + i16::MAX, + -3, + i16::MAX, + -4, + i16::MAX, + -5, + i16::MAX, + -6, + i16::MAX, + -7, + i16::MAX, + -8, + ); + let b = _mm256_setr_epi16( + i16::MIN, + 1, + i16::MIN, + 2, + i16::MIN, + 3, + i16::MIN, + 4, + i16::MIN, + 5, + i16::MIN, + 6, + i16::MIN, + 7, + i16::MIN, + 8, + ); + let expected = _mm256_setr_epi16( + i16::MIN, + i16::MIN + 1, + i16::MIN + 2, + i16::MIN + 3, + i16::MAX, + i16::MAX - 1, + i16::MAX - 2, + i16::MAX - 3, + i16::MIN + 4, + i16::MIN + 5, + i16::MIN + 6, + i16::MIN + 7, + i16::MAX - 4, + i16::MAX - 5, + i16::MAX - 6, + i16::MAX - 7, + ); + let r = _mm256_hsub_epi16(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_hsub_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_hsub_epi32() { + let a = _mm256_set1_epi32(2); + let b = _mm256_set1_epi32(4); + let r = _mm256_hsub_epi32(a, b); + let e = _mm256_set1_epi32(0); + assert_eq_m256i(r, e); + + // Test wrapping on overflow + let a = _mm256_setr_epi32(i32::MAX, -1, i32::MAX, -2, i32::MAX, -3, i32::MAX, -4); + let b = _mm256_setr_epi32(i32::MIN, 1, i32::MIN, 2, i32::MIN, 3, i32::MIN, 4); + let expected = _mm256_setr_epi32( + i32::MIN, + i32::MIN + 1, + i32::MAX, + i32::MAX - 1, + i32::MIN + 2, + i32::MIN + 3, + i32::MAX - 2, + i32::MAX - 3, + ); + let r = _mm256_hsub_epi32(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_hsub_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_hsubs_epi16() { + let a = _mm256_set1_epi16(2); + let a = _mm256_insert_epi16::<0>(a, 0x7fff); + let a = _mm256_insert_epi16::<1>(a, -1); + let b = _mm256_set1_epi16(4); + let r = _mm256_hsubs_epi16(a, b); + let e = _mm256_insert_epi16::<0>(_mm256_set1_epi16(0), 0x7FFF); + assert_eq_m256i(r, e); + + // Test saturating on overflow + let a = _mm256_setr_epi16( + i16::MAX, + -1, + i16::MAX, + -2, + i16::MAX, + -3, + i16::MAX, + -4, + i16::MAX, + -5, + i16::MAX, + -6, + i16::MAX, + -7, + i16::MAX, + -8, + ); + let b = _mm256_setr_epi16( + i16::MIN, + 1, + i16::MIN, + 2, + i16::MIN, + 3, + i16::MIN, + 4, + i16::MIN, + 5, + i16::MIN, + 6, + i16::MIN, + 7, + i16::MIN, + 8, + ); + let expected = _mm256_setr_epi16( + i16::MAX, + i16::MAX, + i16::MAX, + i16::MAX, + i16::MIN, + i16::MIN, + i16::MIN, + i16::MIN, + i16::MAX, + i16::MAX, + i16::MAX, + i16::MAX, + i16::MIN, + i16::MIN, + i16::MIN, + i16::MIN, + ); + let r = _mm256_hsubs_epi16(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_hsubs_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i32gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm_i32gather_epi32::<4>(arr.as_ptr(), _mm_setr_epi32(0, 16, 32, 48)); + assert_eq_m128i(r, _mm_setr_epi32(0, 16, 32, 48)); + } + test_mm_i32gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i32gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm_mask_i32gather_epi32::<4>( + _mm_set1_epi32(256), + arr.as_ptr(), + _mm_setr_epi32(0, 16, 64, 96), + _mm_setr_epi32(-1, -1, -1, 0), + ); + assert_eq_m128i(r, _mm_setr_epi32(0, 16, 64, 256)); + } + test_mm_mask_i32gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i32gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = + _mm256_i32gather_epi32::<4>(arr.as_ptr(), _mm256_setr_epi32(0, 16, 32, 48, 1, 2, 3, 4)); + assert_eq_m256i(r, _mm256_setr_epi32(0, 16, 32, 48, 1, 2, 3, 4)); + } + test_mm256_i32gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i32gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm256_mask_i32gather_epi32::<4>( + _mm256_set1_epi32(256), + arr.as_ptr(), + _mm256_setr_epi32(0, 16, 64, 96, 0, 0, 0, 0), + _mm256_setr_epi32(-1, -1, -1, 0, 0, 0, 0, 0), + ); + assert_eq_m256i(r, _mm256_setr_epi32(0, 16, 64, 256, 256, 256, 256, 256)); + } + test_mm256_mask_i32gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i32gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm_i32gather_ps::<4>(arr.as_ptr(), _mm_setr_epi32(0, 16, 32, 48)); + assert_eq_m128(r, _mm_setr_ps(0.0, 16.0, 32.0, 48.0)); + } + test_mm_i32gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i32gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm_mask_i32gather_ps::<4>( + _mm_set1_ps(256.0), + arr.as_ptr(), + _mm_setr_epi32(0, 16, 64, 96), + _mm_setr_ps(-1.0, -1.0, -1.0, 0.0), + ); + assert_eq_m128(r, _mm_setr_ps(0.0, 16.0, 64.0, 256.0)); + } + test_mm_mask_i32gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i32gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = + _mm256_i32gather_ps::<4>(arr.as_ptr(), _mm256_setr_epi32(0, 16, 32, 48, 1, 2, 3, 4)); + assert_eq_m256(r, _mm256_setr_ps(0.0, 16.0, 32.0, 48.0, 1.0, 2.0, 3.0, 4.0)); + } + test_mm256_i32gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i32gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm256_mask_i32gather_ps::<4>( + _mm256_set1_ps(256.0), + arr.as_ptr(), + _mm256_setr_epi32(0, 16, 64, 96, 0, 0, 0, 0), + _mm256_setr_ps(-1.0, -1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0), + ); + assert_eq_m256(r, _mm256_setr_ps(0.0, 16.0, 64.0, 256.0, 256.0, 256.0, 256.0, 256.0)); + } + test_mm256_mask_i32gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i32gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm_i32gather_epi64::<8>(arr.as_ptr(), _mm_setr_epi32(0, 16, 0, 0)); + assert_eq_m128i(r, _mm_setr_epi64x(0, 16)); + } + test_mm_i32gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i32gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm_mask_i32gather_epi64::<8>( + _mm_set1_epi64x(256), + arr.as_ptr(), + _mm_setr_epi32(16, 16, 16, 16), + _mm_setr_epi64x(-1, 0), + ); + assert_eq_m128i(r, _mm_setr_epi64x(16, 256)); + } + test_mm_mask_i32gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i32gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm256_i32gather_epi64::<8>(arr.as_ptr(), _mm_setr_epi32(0, 16, 32, 48)); + assert_eq_m256i(r, _mm256_setr_epi64x(0, 16, 32, 48)); + } + test_mm256_i32gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i32gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm256_mask_i32gather_epi64::<8>( + _mm256_set1_epi64x(256), + arr.as_ptr(), + _mm_setr_epi32(0, 16, 64, 96), + _mm256_setr_epi64x(-1, -1, -1, 0), + ); + assert_eq_m256i(r, _mm256_setr_epi64x(0, 16, 64, 256)); + } + test_mm256_mask_i32gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i32gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm_i32gather_pd::<8>(arr.as_ptr(), _mm_setr_epi32(0, 16, 0, 0)); + assert_eq_m128d(r, _mm_setr_pd(0.0, 16.0)); + } + test_mm_i32gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i32gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm_mask_i32gather_pd::<8>( + _mm_set1_pd(256.0), + arr.as_ptr(), + _mm_setr_epi32(16, 16, 16, 16), + _mm_setr_pd(-1.0, 0.0), + ); + assert_eq_m128d(r, _mm_setr_pd(16.0, 256.0)); + } + test_mm_mask_i32gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i32gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm256_i32gather_pd::<8>(arr.as_ptr(), _mm_setr_epi32(0, 16, 32, 48)); + assert_eq_m256d(r, _mm256_setr_pd(0.0, 16.0, 32.0, 48.0)); + } + test_mm256_i32gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i32gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm256_mask_i32gather_pd::<8>( + _mm256_set1_pd(256.0), + arr.as_ptr(), + _mm_setr_epi32(0, 16, 64, 96), + _mm256_setr_pd(-1.0, -1.0, -1.0, 0.0), + ); + assert_eq_m256d(r, _mm256_setr_pd(0.0, 16.0, 64.0, 256.0)); + } + test_mm256_mask_i32gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i64gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm_i64gather_epi32::<4>(arr.as_ptr(), _mm_setr_epi64x(0, 16)); + assert_eq_m128i(r, _mm_setr_epi32(0, 16, 0, 0)); + } + test_mm_i64gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i64gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm_mask_i64gather_epi32::<4>( + _mm_set1_epi32(256), + arr.as_ptr(), + _mm_setr_epi64x(0, 16), + _mm_setr_epi32(-1, 0, -1, 0), + ); + assert_eq_m128i(r, _mm_setr_epi32(0, 256, 0, 0)); + } + test_mm_mask_i64gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i64gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm256_i64gather_epi32::<4>(arr.as_ptr(), _mm256_setr_epi64x(0, 16, 32, 48)); + assert_eq_m128i(r, _mm_setr_epi32(0, 16, 32, 48)); + } + test_mm256_i64gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i64gather_epi32() { + let arr: [i32; 128] = core::array::from_fn(|i| i as i32); + // A multiplier of 4 is word-addressing + let r = _mm256_mask_i64gather_epi32::<4>( + _mm_set1_epi32(256), + arr.as_ptr(), + _mm256_setr_epi64x(0, 16, 64, 96), + _mm_setr_epi32(-1, -1, -1, 0), + ); + assert_eq_m128i(r, _mm_setr_epi32(0, 16, 64, 256)); + } + test_mm256_mask_i64gather_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i64gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm_i64gather_ps::<4>(arr.as_ptr(), _mm_setr_epi64x(0, 16)); + assert_eq_m128(r, _mm_setr_ps(0.0, 16.0, 0.0, 0.0)); + } + test_mm_i64gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i64gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm_mask_i64gather_ps::<4>( + _mm_set1_ps(256.0), + arr.as_ptr(), + _mm_setr_epi64x(0, 16), + _mm_setr_ps(-1.0, 0.0, -1.0, 0.0), + ); + assert_eq_m128(r, _mm_setr_ps(0.0, 256.0, 0.0, 0.0)); + } + test_mm_mask_i64gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i64gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm256_i64gather_ps::<4>(arr.as_ptr(), _mm256_setr_epi64x(0, 16, 32, 48)); + assert_eq_m128(r, _mm_setr_ps(0.0, 16.0, 32.0, 48.0)); + } + test_mm256_i64gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i64gather_ps() { + let arr: [f32; 128] = core::array::from_fn(|i| i as f32); + // A multiplier of 4 is word-addressing for f32s + let r = _mm256_mask_i64gather_ps::<4>( + _mm_set1_ps(256.0), + arr.as_ptr(), + _mm256_setr_epi64x(0, 16, 64, 96), + _mm_setr_ps(-1.0, -1.0, -1.0, 0.0), + ); + assert_eq_m128(r, _mm_setr_ps(0.0, 16.0, 64.0, 256.0)); + } + test_mm256_mask_i64gather_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i64gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm_i64gather_epi64::<8>(arr.as_ptr(), _mm_setr_epi64x(0, 16)); + assert_eq_m128i(r, _mm_setr_epi64x(0, 16)); + } + test_mm_i64gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i64gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm_mask_i64gather_epi64::<8>( + _mm_set1_epi64x(256), + arr.as_ptr(), + _mm_setr_epi64x(16, 16), + _mm_setr_epi64x(-1, 0), + ); + assert_eq_m128i(r, _mm_setr_epi64x(16, 256)); + } + test_mm_mask_i64gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i64gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm256_i64gather_epi64::<8>(arr.as_ptr(), _mm256_setr_epi64x(0, 16, 32, 48)); + assert_eq_m256i(r, _mm256_setr_epi64x(0, 16, 32, 48)); + } + test_mm256_i64gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i64gather_epi64() { + let arr: [i64; 128] = core::array::from_fn(|i| i as i64); + // A multiplier of 8 is word-addressing for i64s + let r = _mm256_mask_i64gather_epi64::<8>( + _mm256_set1_epi64x(256), + arr.as_ptr(), + _mm256_setr_epi64x(0, 16, 64, 96), + _mm256_setr_epi64x(-1, -1, -1, 0), + ); + assert_eq_m256i(r, _mm256_setr_epi64x(0, 16, 64, 256)); + } + test_mm256_mask_i64gather_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_i64gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm_i64gather_pd::<8>(arr.as_ptr(), _mm_setr_epi64x(0, 16)); + assert_eq_m128d(r, _mm_setr_pd(0.0, 16.0)); + } + test_mm_i64gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_mask_i64gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm_mask_i64gather_pd::<8>( + _mm_set1_pd(256.0), + arr.as_ptr(), + _mm_setr_epi64x(16, 16), + _mm_setr_pd(-1.0, 0.0), + ); + assert_eq_m128d(r, _mm_setr_pd(16.0, 256.0)); + } + test_mm_mask_i64gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_i64gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm256_i64gather_pd::<8>(arr.as_ptr(), _mm256_setr_epi64x(0, 16, 32, 48)); + assert_eq_m256d(r, _mm256_setr_pd(0.0, 16.0, 32.0, 48.0)); + } + test_mm256_i64gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mask_i64gather_pd() { + let arr: [f64; 128] = core::array::from_fn(|i| i as f64); + // A multiplier of 8 is word-addressing for f64s + let r = _mm256_mask_i64gather_pd::<8>( + _mm256_set1_pd(256.0), + arr.as_ptr(), + _mm256_setr_epi64x(0, 16, 64, 96), + _mm256_setr_pd(-1.0, -1.0, -1.0, 0.0), + ); + assert_eq_m256d(r, _mm256_setr_pd(0.0, 16.0, 64.0, 256.0)); + } + test_mm256_mask_i64gather_pd(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_madd_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(4); + let r = _mm256_madd_epi16(a, b); + let e = _mm256_set1_epi32(16); + assert_eq_m256i(r, e); + } + test_mm256_madd_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_maddubs_epi16() { + let a = _mm256_set1_epi8(2); + let b = _mm256_set1_epi8(4); + let r = _mm256_maddubs_epi16(a, b); + let e = _mm256_set1_epi16(16); + assert_eq_m256i(r, e); + } + test_mm256_maddubs_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_maskload_epi32() { + let nums = [1, 2, 3, 4]; + let a = &nums as *const i32; + let mask = _mm_setr_epi32(-1, 0, 0, -1); + let r = _mm_maskload_epi32(a, mask); + let e = _mm_setr_epi32(1, 0, 0, 4); + assert_eq_m128i(r, e); + + // Unaligned pointer + let a = Unaligned::new([1i32, 2, 3, 4]); + let mask = _mm_setr_epi32(0, !0, 0, !0); + let r = _mm_maskload_epi32(a.as_ptr().cast(), mask); + let e = _mm_setr_epi32(0, 2, 0, 4); + assert_eq_m128i(r, e); + + // Only loading first element, so slice can be short. + let a = &[2i32]; + let mask = _mm_setr_epi32(!0, 0, 0, 0); + let r = _mm_maskload_epi32(a.as_ptr(), mask); + let e = _mm_setr_epi32(2, 0, 0, 0); + assert_eq_m128i(r, e); + + // Only loading last element, so slice can be short. + let a = &[2i32]; + let mask = _mm_setr_epi32(0, 0, 0, !0); + let r = _mm_maskload_epi32(a.as_ptr().wrapping_sub(3), mask); + let e = _mm_setr_epi32(0, 0, 0, 2); + assert_eq_m128i(r, e); + } + test_mm_maskload_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_maskload_epi32() { + let nums = [1, 2, 3, 4, 5, 6, 7, 8]; + let a = &nums as *const i32; + let mask = _mm256_setr_epi32(-1, 0, 0, -1, 0, -1, -1, 0); + let r = _mm256_maskload_epi32(a, mask); + let e = _mm256_setr_epi32(1, 0, 0, 4, 0, 6, 7, 0); + assert_eq_m256i(r, e); + + // Unaligned pointer + let a = Unaligned::new([1i32, 2, 3, 4, 5, 6, 7, 8]); + let mask = _mm256_setr_epi32(0, !0, 0, !0, 0, !0, 0, !0); + let r = _mm256_maskload_epi32(a.as_ptr().cast(), mask); + let e = _mm256_setr_epi32(0, 2, 0, 4, 0, 6, 0, 8); + assert_eq_m256i(r, e); + + // Only loading first element, so slice can be short. + let a = &[2i32]; + let mask = _mm256_setr_epi32(!0, 0, 0, 0, 0, 0, 0, 0); + let r = _mm256_maskload_epi32(a.as_ptr(), mask); + let e = _mm256_setr_epi32(2, 0, 0, 0, 0, 0, 0, 0); + assert_eq_m256i(r, e); + + // Only loading last element, so slice can be short. + let a = &[2i32]; + let mask = _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, !0); + let r = _mm256_maskload_epi32(a.as_ptr().wrapping_sub(7), mask); + let e = _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, 2); + assert_eq_m256i(r, e); + } + test_mm256_maskload_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_maskload_epi64() { + let nums = [1_i64, 2_i64]; + let a = &nums as *const i64; + let mask = _mm_setr_epi64x(0, -1); + let r = _mm_maskload_epi64(a, mask); + let e = _mm_setr_epi64x(0, 2); + assert_eq_m128i(r, e); + + // Unaligned pointer + let a = Unaligned::new([1i64, 2]); + let mask = _mm_setr_epi64x(0, !0); + let r = _mm_maskload_epi64(a.as_ptr().cast(), mask); + let e = _mm_setr_epi64x(0, 2); + assert_eq_m128i(r, e); + + // Only loading first element, so slice can be short. + let a = &[2i64]; + let mask = _mm_setr_epi64x(!0, 0); + let r = _mm_maskload_epi64(a.as_ptr(), mask); + let e = _mm_setr_epi64x(2, 0); + assert_eq_m128i(r, e); + + // Only loading last element, so slice can be short. + let a = &[2i64]; + let mask = _mm_setr_epi64x(0, !0); + let r = _mm_maskload_epi64(a.as_ptr().wrapping_sub(1), mask); + let e = _mm_setr_epi64x(0, 2); + assert_eq_m128i(r, e); + } + test_mm_maskload_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_maskload_epi64() { + let nums = [1_i64, 2_i64, 3_i64, 4_i64]; + let a = &nums as *const i64; + let mask = _mm256_setr_epi64x(0, -1, -1, 0); + let r = _mm256_maskload_epi64(a, mask); + let e = _mm256_setr_epi64x(0, 2, 3, 0); + assert_eq_m256i(r, e); + + // Unaligned pointer + let a = Unaligned::new([1i64, 2, 3, 4]); + let mask = _mm256_setr_epi64x(0, !0, 0, !0); + let r = _mm256_maskload_epi64(a.as_ptr().cast(), mask); + let e = _mm256_setr_epi64x(0, 2, 0, 4); + assert_eq_m256i(r, e); + + // Only loading first element, so slice can be short. + let a = &[2i64]; + let mask = _mm256_setr_epi64x(!0, 0, 0, 0); + let r = _mm256_maskload_epi64(a.as_ptr(), mask); + let e = _mm256_setr_epi64x(2, 0, 0, 0); + assert_eq_m256i(r, e); + + // Only loading last element, so slice can be short. + let a = &[2i64]; + let mask = _mm256_setr_epi64x(0, 0, 0, !0); + let r = _mm256_maskload_epi64(a.as_ptr().wrapping_sub(3), mask); + let e = _mm256_setr_epi64x(0, 0, 0, 2); + assert_eq_m256i(r, e); + } + test_mm256_maskload_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_maskstore_epi32() { + let a = _mm_setr_epi32(1, 2, 3, 4); + let mut arr = [-1, -1, -1, -1]; + let mask = _mm_setr_epi32(-1, 0, 0, -1); + _mm_maskstore_epi32(arr.as_mut_ptr(), mask, a); + let e = [1, -1, -1, 4]; + assert_eq!(arr, e); + + // Unaligned pointer + let mut r = Unaligned::new([0i32; 4]); + let mask = _mm_setr_epi32(0, !0, 0, !0); + let a = _mm_setr_epi32(1, 2, 3, 4); + _mm_maskstore_epi32(r.as_mut_ptr().cast(), mask, a); + let e = [0i32, 2, 0, 4]; + assert_eq!(r.read(), e); + + // Only storing first element, so slice can be short. + let mut r = [0i32]; + let mask = _mm_setr_epi32(!0, 0, 0, 0); + let a = _mm_setr_epi32(1, 2, 3, 4); + _mm_maskstore_epi32(r.as_mut_ptr(), mask, a); + let e = [1i32]; + assert_eq!(r, e); + + // Only storing last element, so slice can be short. + let mut r = [0i32]; + let mask = _mm_setr_epi32(0, 0, 0, !0); + let a = _mm_setr_epi32(1, 2, 3, 4); + _mm_maskstore_epi32(r.as_mut_ptr().wrapping_sub(3), mask, a); + let e = [4i32]; + assert_eq!(r, e); + } + test_mm_maskstore_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_maskstore_epi32() { + let a = _mm256_setr_epi32(1, 0x6d726f, 3, 42, 0x777161, 6, 7, 8); + let mut arr = [-1, -1, -1, 0x776173, -1, 0x68657265, -1, -1]; + let mask = _mm256_setr_epi32(-1, 0, 0, -1, 0, -1, -1, 0); + _mm256_maskstore_epi32(arr.as_mut_ptr(), mask, a); + let e = [1, -1, -1, 42, -1, 6, 7, -1]; + assert_eq!(arr, e); + + // Unaligned pointer + let mut r = Unaligned::new([0i32; 8]); + let mask = _mm256_setr_epi32(0, !0, 0, !0, 0, !0, 0, !0); + let a = _mm256_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8); + _mm256_maskstore_epi32(r.as_mut_ptr().cast(), mask, a); + let e = [0i32, 2, 0, 4, 0, 6, 0, 8]; + assert_eq!(r.read(), e); + + // Only storing first element, so slice can be short. + let mut r = [0i32]; + let mask = _mm256_setr_epi32(!0, 0, 0, 0, 0, 0, 0, 0); + let a = _mm256_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8); + _mm256_maskstore_epi32(r.as_mut_ptr(), mask, a); + let e = [1i32]; + assert_eq!(r, e); + + // Only storing last element, so slice can be short. + let mut r = [0i32]; + let mask = _mm256_setr_epi32(0, 0, 0, 0, 0, 0, 0, !0); + let a = _mm256_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8); + _mm256_maskstore_epi32(r.as_mut_ptr().wrapping_sub(7), mask, a); + let e = [8i32]; + assert_eq!(r, e); + } + test_mm256_maskstore_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_maskstore_epi64() { + let a = _mm_setr_epi64x(1_i64, 2_i64); + let mut arr = [-1_i64, -1_i64]; + let mask = _mm_setr_epi64x(0, -1); + _mm_maskstore_epi64(arr.as_mut_ptr(), mask, a); + let e = [-1, 2]; + assert_eq!(arr, e); + + // Unaligned pointer + let mut r = Unaligned::new([0i64; 2]); + let mask = _mm_setr_epi64x(0, !0); + let a = _mm_setr_epi64x(1, 2); + _mm_maskstore_epi64(r.as_mut_ptr().cast(), mask, a); + let e = [0i64, 2]; + assert_eq!(r.read(), e); + + // Only storing first element, so slice can be short. + let mut r = [0i64]; + let mask = _mm_setr_epi64x(!0, 0); + let a = _mm_setr_epi64x(1, 2); + _mm_maskstore_epi64(r.as_mut_ptr(), mask, a); + let e = [1i64]; + assert_eq!(r, e); + + // Only storing last element, so slice can be short. + let mut r = [0i64]; + let mask = _mm_setr_epi64x(0, !0); + let a = _mm_setr_epi64x(1, 2); + _mm_maskstore_epi64(r.as_mut_ptr().wrapping_sub(1), mask, a); + let e = [2i64]; + assert_eq!(r, e); + } + test_mm_maskstore_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_maskstore_epi64() { + let a = _mm256_setr_epi64x(1_i64, 2_i64, 3_i64, 4_i64); + let mut arr = [-1_i64, -1_i64, -1_i64, -1_i64]; + let mask = _mm256_setr_epi64x(0, -1, -1, 0); + _mm256_maskstore_epi64(arr.as_mut_ptr(), mask, a); + let e = [-1, 2, 3, -1]; + assert_eq!(arr, e); + + // Unaligned pointer + let mut r = Unaligned::new([0i64; 4]); + let mask = _mm256_setr_epi64x(0, !0, 0, !0); + let a = _mm256_setr_epi64x(1, 2, 3, 4); + _mm256_maskstore_epi64(r.as_mut_ptr().cast(), mask, a); + let e = [0i64, 2, 0, 4]; + assert_eq!(r.read(), e); + + // Only storing first element, so slice can be short. + let mut r = [0i64]; + let mask = _mm256_setr_epi64x(!0, 0, 0, 0); + let a = _mm256_setr_epi64x(1, 2, 3, 4); + _mm256_maskstore_epi64(r.as_mut_ptr(), mask, a); + let e = [1i64]; + assert_eq!(r, e); + + // Only storing last element, so slice can be short. + let mut r = [0i64]; + let mask = _mm256_setr_epi64x(0, 0, 0, !0); + let a = _mm256_setr_epi64x(1, 2, 3, 4); + _mm256_maskstore_epi64(r.as_mut_ptr().wrapping_sub(3), mask, a); + let e = [4i64]; + assert_eq!(r, e); + } + test_mm256_maskstore_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mpsadbw_epu8() { + let a = _mm256_setr_epi8( + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, + 18, 20, 22, 24, 26, 28, 30, + ); + + let r = _mm256_mpsadbw_epu8::<0b000>(a, a); + let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b001>(a, a); + let e = _mm256_setr_epi16(16, 12, 8, 4, 0, 4, 8, 12, 32, 24, 16, 8, 0, 8, 16, 24); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b100>(a, a); + let e = _mm256_setr_epi16(16, 20, 24, 28, 32, 36, 40, 44, 32, 40, 48, 56, 64, 72, 80, 88); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b101>(a, a); + let e = _mm256_setr_epi16(0, 4, 8, 12, 16, 20, 24, 28, 0, 8, 16, 24, 32, 40, 48, 56); + assert_eq_m256i(r, e); + + let r = _mm256_mpsadbw_epu8::<0b111>(a, a); + let e = _mm256_setr_epi16(32, 28, 24, 20, 16, 12, 8, 4, 64, 56, 48, 40, 32, 24, 16, 8); + assert_eq_m256i(r, e); + } + test_mm256_mpsadbw_epu8(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_mulhrs_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(4); + let r = _mm256_mullo_epi16(a, b); + let e = _mm256_set1_epi16(8); + assert_eq_m256i(r, e); + } + test_mm256_mulhrs_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_packs_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(4); + let r = _mm256_packs_epi16(a, b); + #[rustfmt::skip] + let e = _mm256_setr_epi8( + 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, + 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, + ); + + assert_eq_m256i(r, e); + } + test_mm256_packs_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_packs_epi32() { + let a = _mm256_set1_epi32(2); + let b = _mm256_set1_epi32(4); + let r = _mm256_packs_epi32(a, b); + let e = _mm256_setr_epi16(2, 2, 2, 2, 4, 4, 4, 4, 2, 2, 2, 2, 4, 4, 4, 4); + + assert_eq_m256i(r, e); + } + test_mm256_packs_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_packus_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(4); + let r = _mm256_packus_epi16(a, b); + #[rustfmt::skip] + let e = _mm256_setr_epi8( + 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, + 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, + ); + + assert_eq_m256i(r, e); + } + test_mm256_packus_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_packus_epi32() { + let a = _mm256_set1_epi32(2); + let b = _mm256_set1_epi32(4); + let r = _mm256_packus_epi32(a, b); + let e = _mm256_setr_epi16(2, 2, 2, 2, 4, 4, 4, 4, 2, 2, 2, 2, 4, 4, 4, 4); + + assert_eq_m256i(r, e); + } + test_mm256_packus_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_permutevar8x32_epi32() { + let a = _mm256_setr_epi32(100, 200, 300, 400, 500, 600, 700, 800); + let b = _mm256_setr_epi32(5, 0, 5, 1, 7, 6, 3, 4); + let expected = _mm256_setr_epi32(600, 100, 600, 200, 800, 700, 400, 500); + let r = _mm256_permutevar8x32_epi32(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_permutevar8x32_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_permute2x128_si256() { + let a = _mm256_setr_epi64x(100, 200, 500, 600); + let b = _mm256_setr_epi64x(300, 400, 700, 800); + let r = _mm256_permute2x128_si256::<0b00_01_00_11>(a, b); + let e = _mm256_setr_epi64x(700, 800, 500, 600); + assert_eq_m256i(r, e); + } + test_mm256_permute2x128_si256(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_permutevar8x32_ps() { + let a = _mm256_setr_ps(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm256_setr_epi32(5, 0, 5, 1, 7, 6, 3, 4); + let r = _mm256_permutevar8x32_ps(a, b); + let e = _mm256_setr_ps(6., 1., 6., 2., 8., 7., 4., 5.); + assert_eq_m256(r, e); + } + test_mm256_permutevar8x32_ps(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sad_epu8() { + let a = _mm256_set1_epi8(2); + let b = _mm256_set1_epi8(4); + let r = _mm256_sad_epu8(a, b); + let e = _mm256_set1_epi64x(16); + assert_eq_m256i(r, e); + } + test_mm256_sad_epu8(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_shuffle_epi8() { + #[rustfmt::skip] + let a = _mm256_setr_epi8( + 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + ); + #[rustfmt::skip] + let b = _mm256_setr_epi8( + 4, 128u8 as i8, 4, 3, 24, 12, 6, 19, + 12, 5, 5, 10, 4, 1, 8, 0, + 4, 128u8 as i8, 4, 3, 24, 12, 6, 19, + 12, 5, 5, 10, 4, 1, 8, 0, + ); + #[rustfmt::skip] + let expected = _mm256_setr_epi8( + 5, 0, 5, 4, 9, 13, 7, 4, + 13, 6, 6, 11, 5, 2, 9, 1, + 21, 0, 21, 20, 25, 29, 23, 20, + 29, 22, 22, 27, 21, 18, 25, 17, + ); + let r = _mm256_shuffle_epi8(a, b); + assert_eq_m256i(r, expected); + } + test_mm256_shuffle_epi8(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sign_epi16() { + let a = _mm256_set1_epi16(2); + let b = _mm256_set1_epi16(-1); + let r = _mm256_sign_epi16(a, b); + let e = _mm256_set1_epi16(-2); + assert_eq_m256i(r, e); + } + test_mm256_sign_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sign_epi32() { + let a = _mm256_set1_epi32(2); + let b = _mm256_set1_epi32(-1); + let r = _mm256_sign_epi32(a, b); + let e = _mm256_set1_epi32(-2); + assert_eq_m256i(r, e); + } + test_mm256_sign_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sign_epi8() { + let a = _mm256_set1_epi8(2); + let b = _mm256_set1_epi8(-1); + let r = _mm256_sign_epi8(a, b); + let e = _mm256_set1_epi8(-2); + assert_eq_m256i(r, e); + } + test_mm256_sign_epi8(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sll_epi16() { + let a = _mm256_setr_epi16( + 0x88, -0x88, 0x99, -0x99, 0xAA, -0xAA, 0xBB, -0xBB, 0xCC, -0xCC, 0xDD, -0xDD, 0xEE, + -0xEE, 0xFF, -0xFF, + ); + let r = _mm256_sll_epi16(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_setr_epi16( + 0x880, -0x880, 0x990, -0x990, 0xAA0, -0xAA0, 0xBB0, -0xBB0, 0xCC0, -0xCC0, 0xDD0, + -0xDD0, 0xEE0, -0xEE0, 0xFF0, -0xFF0, + ), + ); + let r = _mm256_sll_epi16(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_sll_epi16(a, _mm_set_epi64x(0, 16)); + assert_eq_m256i(r, _mm256_set1_epi16(0)); + let r = _mm256_sll_epi16(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_set1_epi16(0)); + } + test_mm256_sll_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sll_epi32() { + let a = + _mm256_setr_epi32(0xCCCC, -0xCCCC, 0xDDDD, -0xDDDD, 0xEEEE, -0xEEEE, 0xFFFF, -0xFFFF); + let r = _mm256_sll_epi32(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_setr_epi32( + 0xCCCC0, -0xCCCC0, 0xDDDD0, -0xDDDD0, 0xEEEE0, -0xEEEE0, 0xFFFF0, -0xFFFF0, + ), + ); + let r = _mm256_sll_epi32(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_sll_epi32(a, _mm_set_epi64x(0, 32)); + assert_eq_m256i(r, _mm256_set1_epi32(0)); + let r = _mm256_sll_epi32(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_set1_epi32(0)); + } + test_mm256_sll_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sll_epi64() { + let a = _mm256_set_epi64x(0xEEEEEEEE, -0xEEEEEEEE, 0xFFFFFFFF, -0xFFFFFFFF); + let r = _mm256_sll_epi64(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i(r, _mm256_set_epi64x(0xEEEEEEEE0, -0xEEEEEEEE0, 0xFFFFFFFF0, -0xFFFFFFFF0)); + let r = _mm256_sll_epi64(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_sll_epi64(a, _mm_set_epi64x(0, 64)); + assert_eq_m256i(r, _mm256_set1_epi64x(0)); + let r = _mm256_sll_epi64(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_set1_epi64x(0)); + } + test_mm256_sll_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sra_epi16() { + let a = _mm256_setr_epi16( + 0x88, -0x88, 0x99, -0x99, 0xAA, -0xAA, 0xBB, -0xBB, 0xCC, -0xCC, 0xDD, -0xDD, 0xEE, + -0xEE, 0xFF, -0xFF, + ); + let r = _mm256_sra_epi16(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_setr_epi16( + 0x8, -0x9, 0x9, -0xA, 0xA, -0xB, 0xB, -0xC, 0xC, -0xD, 0xD, -0xE, 0xE, -0xF, 0xF, + -0x10, + ), + ); + let r = _mm256_sra_epi16(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_sra_epi16(a, _mm_set_epi64x(0, 16)); + assert_eq_m256i( + r, + _mm256_setr_epi16(0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1), + ); + let r = _mm256_sra_epi16(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i( + r, + _mm256_setr_epi16(0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1), + ); + } + test_mm256_sra_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sra_epi32() { + let a = + _mm256_setr_epi32(0xCCCC, -0xCCCC, 0xDDDD, -0xDDDD, 0xEEEE, -0xEEEE, 0xFFFF, -0xFFFF); + let r = _mm256_sra_epi32(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_setr_epi32(0xCCC, -0xCCD, 0xDDD, -0xDDE, 0xEEE, -0xEEF, 0xFFF, -0x1000), + ); + let r = _mm256_sra_epi32(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_sra_epi32(a, _mm_set_epi64x(0, 32)); + assert_eq_m256i(r, _mm256_setr_epi32(0, -1, 0, -1, 0, -1, 0, -1)); + let r = _mm256_sra_epi32(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_setr_epi32(0, -1, 0, -1, 0, -1, 0, -1)); + } + test_mm256_sra_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_srl_epi16() { + let a = _mm256_setr_epi16( + 0x88, -0x88, 0x99, -0x99, 0xAA, -0xAA, 0xBB, -0xBB, 0xCC, -0xCC, 0xDD, -0xDD, 0xEE, + -0xEE, 0xFF, -0xFF, + ); + let r = _mm256_srl_epi16(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_setr_epi16( + 0x8, 0xFF7, 0x9, 0xFF6, 0xA, 0xFF5, 0xB, 0xFF4, 0xC, 0xFF3, 0xD, 0xFF2, 0xE, 0xFF1, + 0xF, 0xFF0, + ), + ); + let r = _mm256_srl_epi16(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_srl_epi16(a, _mm_set_epi64x(0, 16)); + assert_eq_m256i(r, _mm256_set1_epi16(0)); + let r = _mm256_srl_epi16(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_set1_epi16(0)); + } + test_mm256_srl_epi16(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_srl_epi32() { + let a = + _mm256_setr_epi32(0xCCCC, -0xCCCC, 0xDDDD, -0xDDDD, 0xEEEE, -0xEEEE, 0xFFFF, -0xFFFF); + let r = _mm256_srl_epi32(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_setr_epi32( + 0xCCC, 0xFFFF333, 0xDDD, 0xFFFF222, 0xEEE, 0xFFFF111, 0xFFF, 0xFFFF000, + ), + ); + let r = _mm256_srl_epi32(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_srl_epi32(a, _mm_set_epi64x(0, 32)); + assert_eq_m256i(r, _mm256_set1_epi32(0)); + let r = _mm256_srl_epi32(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_set1_epi32(0)); + } + test_mm256_srl_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_srl_epi64() { + let a = _mm256_set_epi64x(0xEEEEEEEE, -0xEEEEEEEE, 0xFFFFFFFF, -0xFFFFFFFF); + let r = _mm256_srl_epi64(a, _mm_set_epi64x(0, 4)); + assert_eq_m256i( + r, + _mm256_set_epi64x(0xEEEEEEE, 0xFFFFFFFF1111111, 0xFFFFFFF, 0xFFFFFFFF0000000), + ); + let r = _mm256_srl_epi64(a, _mm_set_epi64x(4, 0)); + assert_eq_m256i(r, a); + let r = _mm256_srl_epi64(a, _mm_set_epi64x(0, 64)); + assert_eq_m256i(r, _mm256_set1_epi64x(0)); + let r = _mm256_srl_epi64(a, _mm_set_epi64x(0, i64::MAX)); + assert_eq_m256i(r, _mm256_set1_epi64x(0)); + } + test_mm256_srl_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_sllv_epi32() { + let a = _mm_set_epi32(1, 2, 3, 4); + let b = _mm_set_epi32(4, 3, 2, 1); + let r = _mm_sllv_epi32(a, b); + let e = _mm_set_epi32(16, 16, 12, 8); + assert_eq_m128i(r, e); + } + test_mm_sllv_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sllv_epi32() { + let a = _mm256_set_epi32(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm256_set_epi32(8, 7, 6, 5, 4, 3, 2, 1); + let r = _mm256_sllv_epi32(a, b); + let e = _mm256_set_epi32(256, 256, 192, 128, 80, 48, 28, 16); + assert_eq_m256i(r, e); + } + test_mm256_sllv_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_sllv_epi64() { + let a = _mm_set_epi64x(2, 3); + let b = _mm_set_epi64x(1, 2); + let r = _mm_sllv_epi64(a, b); + let e = _mm_set_epi64x(4, 12); + assert_eq_m128i(r, e); + } + test_mm_sllv_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_sllv_epi64() { + let a = _mm256_set_epi64x(1, 2, 3, 4); + let b = _mm256_set_epi64x(4, 3, 2, 1); + let r = _mm256_sllv_epi64(a, b); + let e = _mm256_set_epi64x(16, 16, 12, 8); + assert_eq_m256i(r, e); + } + test_mm256_sllv_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_srav_epi32() { + let a = _mm_set_epi32(16, -32, 64, -128); + let b = _mm_set_epi32(4, 3, 2, 1); + let r = _mm_srav_epi32(a, b); + let e = _mm_set_epi32(1, -4, 16, -64); + assert_eq_m128i(r, e); + } + test_mm_srav_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_srav_epi32() { + let a = _mm256_set_epi32(256, -512, 1024, -2048, 4096, -8192, 16384, -32768); + let b = _mm256_set_epi32(8, 7, 6, 5, 4, 3, 2, 1); + let r = _mm256_srav_epi32(a, b); + let e = _mm256_set_epi32(1, -4, 16, -64, 256, -1024, 4096, -16384); + assert_eq_m256i(r, e); + } + test_mm256_srav_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_srlv_epi32() { + let a = _mm_set_epi32(16, 32, 64, 128); + let b = _mm_set_epi32(4, 3, 2, 1); + let r = _mm_srlv_epi32(a, b); + let e = _mm_set_epi32(1, 4, 16, 64); + assert_eq_m128i(r, e); + } + test_mm_srlv_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_srlv_epi32() { + let a = _mm256_set_epi32(256, 512, 1024, 2048, 4096, 8192, 16384, 32768); + let b = _mm256_set_epi32(8, 7, 6, 5, 4, 3, 2, 1); + let r = _mm256_srlv_epi32(a, b); + let e = _mm256_set_epi32(1, 4, 16, 64, 256, 1024, 4096, 16384); + assert_eq_m256i(r, e); + } + test_mm256_srlv_epi32(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm_srlv_epi64() { + let a = _mm_set_epi64x(4, 8); + let b = _mm_set_epi64x(2, 1); + let r = _mm_srlv_epi64(a, b); + let e = _mm_set_epi64x(1, 4); + assert_eq_m128i(r, e); + } + test_mm_srlv_epi64(); + + #[target_feature(enable = "avx2")] + unsafe fn test_mm256_srlv_epi64() { + let a = _mm256_set_epi64x(16, 32, 64, 128); + let b = _mm256_set_epi64x(4, 3, 2, 1); + let r = _mm256_srlv_epi64(a, b); + let e = _mm256_set_epi64x(1, 4, 16, 64); + assert_eq_m256i(r, e); + } + test_mm256_srlv_epi64(); +} + +#[target_feature(enable = "sse2")] +unsafe fn _mm_setr_epi64x(a: i64, b: i64) -> __m128i { + _mm_set_epi64x(b, a) +} + +#[track_caller] +#[target_feature(enable = "sse")] +unsafe fn assert_eq_m128(a: __m128, b: __m128) { + let r = _mm_cmpeq_ps(a, b); + if _mm_movemask_ps(r) != 0b1111 { + panic!("{:?} != {:?}", a, b); + } +} + +#[track_caller] +#[target_feature(enable = "sse2")] +unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) { + if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 { + panic!("{:?} != {:?}", a, b); + } +} + +#[track_caller] +#[target_feature(enable = "sse2")] +unsafe fn assert_eq_m128i(a: __m128i, b: __m128i) { + assert_eq!(transmute::<_, [u64; 2]>(a), transmute::<_, [u64; 2]>(b)) +} + +#[track_caller] +#[target_feature(enable = "avx")] +unsafe fn assert_eq_m256(a: __m256, b: __m256) { + let cmp = _mm256_cmp_ps::<_CMP_EQ_OQ>(a, b); + if _mm256_movemask_ps(cmp) != 0b11111111 { + panic!("{:?} != {:?}", a, b); + } +} + +#[track_caller] +#[target_feature(enable = "avx")] +unsafe fn assert_eq_m256d(a: __m256d, b: __m256d) { + let cmp = _mm256_cmp_pd::<_CMP_EQ_OQ>(a, b); + if _mm256_movemask_pd(cmp) != 0b1111 { + panic!("{:?} != {:?}", a, b); + } +} + +#[track_caller] +#[target_feature(enable = "avx")] +unsafe fn assert_eq_m256i(a: __m256i, b: __m256i) { + assert_eq!(transmute::<_, [u64; 4]>(a), transmute::<_, [u64; 4]>(b)) +} + +/// Stores `T` in an unaligned address +struct Unaligned { + buf: Vec, + offset: bool, + _marker: std::marker::PhantomData, +} + +impl Unaligned { + fn new(value: T) -> Self { + // Allocate extra byte for unalignment headroom + let len = std::mem::size_of::(); + let mut buf = Vec::::with_capacity(len + 1); + // Force the address to be a non-multiple of 2, so it is as unaligned as it can get. + let offset = (buf.as_ptr() as usize % 2) == 0; + let value_ptr: *const T = &value; + unsafe { + buf.as_mut_ptr().add(offset.into()).copy_from_nonoverlapping(value_ptr.cast(), len); + } + Self { buf, offset, _marker: std::marker::PhantomData } + } + + fn as_ptr(&self) -> *const T { + unsafe { self.buf.as_ptr().add(self.offset.into()).cast() } + } + + fn as_mut_ptr(&mut self) -> *mut T { + unsafe { self.buf.as_mut_ptr().add(self.offset.into()).cast() } + } + + fn read(&self) -> T { + unsafe { self.as_ptr().read_unaligned() } + } +} From af9fc5da1006f3af2ba38e9b4805bbbc110bc748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Tue, 23 Apr 2024 17:47:46 +0200 Subject: [PATCH 081/208] Configure clippy not to generate warnings about arithmetic operations on `rustc_target::abi::Size` --- clippy.toml | 1 + src/shims/x86/mod.rs | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 clippy.toml diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000000..284e18a45a --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +arithmetic-side-effects-allowed = ["rustc_target::abi::Size"] diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index a9d248c2a8..cf3c3758cd 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -985,8 +985,6 @@ fn mask_load<'tcx>( let dest = this.project_index(&dest, i)?; if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { - // Size * u64 is implemented as always checked - #[allow(clippy::arithmetic_side_effects)] let ptr = ptr.wrapping_offset(dest.layout.size * i, &this.tcx); // Unaligned copy, which is what we want. this.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; @@ -1020,8 +1018,6 @@ fn mask_store<'tcx>( let value = this.project_index(&value, i)?; if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 { - // Size * u64 is implemented as always checked - #[allow(clippy::arithmetic_side_effects)] let ptr = ptr.wrapping_offset(value.layout.size * i, &this.tcx); // Unaligned copy, which is what we want. this.mem_copy(value.ptr(), ptr, value.layout.size, /*nonoverlapping*/ true)?; From a79b1f1f5348ff74bcda4fb94aa42b626f8f23bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Tue, 23 Apr 2024 19:23:15 +0200 Subject: [PATCH 082/208] Fix wording in shift functions doc comments --- src/shims/x86/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index cf3c3758cd..cd4e1c2e00 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -488,7 +488,7 @@ enum ShiftOp { /// /// For logic shifts, when right is larger than BITS - 1, zero is produced. /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign -/// bit is copied to remaining bits. +/// bit is copied to all bits. fn shift_simd_by_scalar<'tcx>( this: &mut crate::MiriInterpCx<'_, 'tcx>, left: &OpTy<'tcx, Provenance>, @@ -544,7 +544,7 @@ fn shift_simd_by_scalar<'tcx>( /// /// For logic shifts, when right is larger than BITS - 1, zero is produced. /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign -/// bit is copied to remaining bits. +/// bit is copied to all bits. fn shift_simd_by_simd<'tcx>( this: &mut crate::MiriInterpCx<'_, 'tcx>, left: &OpTy<'tcx, Provenance>, From 7be87b23a5efbd8d1cb5abe613da03f65e2e319a Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 24 Apr 2024 04:57:24 +0000 Subject: [PATCH 083/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 9b0b1c8d23..3191355ccb 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -aca749eefceaed0cda19a7ec5e472fce9387bc00 +c1feb3eceef7d5f0126c309a87062cf413fe0a25 From 1b32764ca93ac8ee6a3eecb14aa0d33aef2c62f4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 09:34:49 +0200 Subject: [PATCH 084/208] windows: basic support for GetUserProfileDirectoryW --- Cargo.lock | 49 +++++++++++++++++++++++++ Cargo.toml | 1 + src/shims/env.rs | 58 +++++++++++++++++++++++++++++- src/shims/windows/foreign_items.rs | 6 ++++ tests/pass/shims/env/home.rs | 2 +- 5 files changed, 114 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e6b5502b0..293b937a5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,6 +299,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -490,6 +511,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.4.2", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -558,6 +589,7 @@ dependencies = [ "chrono", "colored", "ctrlc", + "directories", "getrandom", "jemalloc-sys", "lazy_static", @@ -614,6 +646,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "owo-colors" version = "3.5.0" @@ -746,6 +784,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.3" diff --git a/Cargo.toml b/Cargo.toml index 7748d630b1..b00dae784d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ aes = { version = "0.8.3", features = ["hazmat"] } measureme = "11" ctrlc = "3.2.5" chrono = { version = "0.4.38", default-features = false, features = ["clock"] } +directories = "5" # Copied from `compiler/rustc/Cargo.toml`. # But only for some targets, it fails for others. Rustc configures this in its CI, but we can't diff --git a/src/shims/env.rs b/src/shims/env.rs index d97873ce72..22571d0c1c 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -494,9 +494,65 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> { let this = self.eval_context_mut(); this.assert_target_os("windows", "GetCurrentProcessId"); - this.check_no_isolation("`GetCurrentProcessId`")?; Ok(std::process::id()) } + + #[allow(non_snake_case)] + fn GetUserProfileDirectoryW( + &mut self, + token: &OpTy<'tcx, Provenance>, // HANDLE + buf: &OpTy<'tcx, Provenance>, // LPWSTR + size: &OpTy<'tcx, Provenance>, // LPDWORD + ) -> InterpResult<'tcx, Scalar> // returns BOOL + { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetUserProfileDirectoryW"); + this.check_no_isolation("`GetUserProfileDirectoryW`")?; + + let token = this.read_target_isize(token)?; + let buf = this.read_pointer(buf)?; + let size = this.deref_pointer(size)?; + + if token != -4 { + throw_unsup_format!( + "GetUserProfileDirectoryW: only CURRENT_PROCESS_TOKEN is supported" + ); + } + + // See for docs. + Ok(match directories::UserDirs::new() { + Some(dirs) => { + let home = dirs.home_dir(); + let size_avail = if this.ptr_is_null(size.ptr())? { + 0 // if the buf pointer is null, we can't write to it; `size` will be updated to the required length + } else { + this.read_scalar(&size)?.to_u32()? + }; + // Of course we cannot use `windows_check_buffer_size` here since this uses + // a different method for dealing with a too-small buffer than the other functions... + let (success, len) = this.write_path_to_wide_str( + home, + buf, + size_avail.into(), + /*truncate*/ false, + )?; + // The Windows docs just say that this is written on failure. But std + // seems to rely on it always being written. + this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?; + if success { + Scalar::from_i32(1) // return TRUE + } else { + this.set_last_error(this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER"))?; + Scalar::from_i32(0) // return FALSE + } + } + None => { + // We have to pick some error code. + this.set_last_error(this.eval_windows("c", "ERROR_BAD_USER_PROFILE"))?; + Scalar::from_i32(0) // return FALSE + } + }) + } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index c81d6b2f7f..cf35ecec7c 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -135,6 +135,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.SetCurrentDirectoryW(path)?; this.write_scalar(result, dest)?; } + "GetUserProfileDirectoryW" => { + let [token, buf, size] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + let result = this.GetUserProfileDirectoryW(token, buf, size)?; + this.write_scalar(result, dest)?; + } // File related shims "NtWriteFile" => { diff --git a/tests/pass/shims/env/home.rs b/tests/pass/shims/env/home.rs index 9eb9c3af56..c237f9ed9f 100644 --- a/tests/pass/shims/env/home.rs +++ b/tests/pass/shims/env/home.rs @@ -1,9 +1,9 @@ -//@ignore-target-windows: home_dir is not supported on Windows //@compile-flags: -Zmiri-disable-isolation use std::env; fn main() { env::remove_var("HOME"); // make sure we enter the interesting codepath + env::remove_var("USERPROFILE"); // Windows also looks as this env var #[allow(deprecated)] env::home_dir().unwrap(); } From b02fe32068ee95e8de29f7ad8bd8274fb3f49410 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 09:41:55 +0200 Subject: [PATCH 085/208] windows buffer size protocol: turns out std resets last_error to 0; let's require that in general --- src/shims/env.rs | 23 +++++++++++++---------- src/shims/windows/foreign_items.rs | 3 ++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/shims/env.rs b/src/shims/env.rs index 22571d0c1c..402e267088 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -160,10 +160,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.assert_target_os("windows", "GetEnvironmentVariableW"); let name_ptr = this.read_pointer(name_op)?; + let buf_ptr = this.read_pointer(buf_op)?; + let buf_size = this.read_scalar(size_op)?.to_u32()?; // in characters + let name = this.read_os_str_from_wide_str(name_ptr)?; Ok(match this.machine.env_vars.map.get(&name) { Some(&var_ptr) => { - this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error // The offset is used to strip the "{name}=" part of the string. #[rustfmt::skip] let name_offset_bytes = u64::try_from(name.len()).unwrap() @@ -172,14 +174,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let var_ptr = var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?; let var = this.read_os_str_from_wide_str(var_ptr)?; - let buf_ptr = this.read_pointer(buf_op)?; - // `buf_size` represents the size in characters. - let buf_size = u64::from(this.read_scalar(size_op)?.to_u32()?); - Scalar::from_u32(windows_check_buffer_size( - this.write_os_str_to_wide_str( - &var, buf_ptr, buf_size, /*truncate*/ false, - )?, - )) + Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str( + &var, + buf_ptr, + buf_size.into(), + /*truncate*/ false, + )?)) + // This can in fact return 0. It is up to the caller to set last_error to 0 + // beforehand and check it afterwards to exclude that case. } None => { let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND"); @@ -375,7 +377,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // If we cannot get the current directory, we return 0 match env::current_dir() { Ok(cwd) => { - this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error + // This can in fact return 0. It is up to the caller to set last_error to 0 + // beforehand and check it afterwards to exclude that case. return Ok(Scalar::from_u32(windows_check_buffer_size( this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?, ))); diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index cf35ecec7c..e0cca43d26 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -231,7 +231,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Scalar::from_u32(0) // return zero upon failure } Ok(abs_filename) => { - this.set_last_error(Scalar::from_u32(0))?; // make sure this is unambiguously not an error Scalar::from_u32(helpers::windows_check_buffer_size( this.write_path_to_wide_str( &abs_filename, @@ -240,6 +239,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /*truncate*/ false, )?, )) + // This can in fact return 0. It is up to the caller to set last_error to 0 + // beforehand and check it afterwards to exclude that case. } }; this.write_scalar(result, dest)?; From 9c48ce553981d6e17a8e40911f50660c7ab79c93 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 09:56:56 +0200 Subject: [PATCH 086/208] make the obsucre truncating variant of this.write_os_str_to_wide_str a non-default function --- src/shims/env.rs | 10 +---- src/shims/os_str.rs | 68 ++++++++++++++++++++---------- src/shims/windows/foreign_items.rs | 25 +++-------- 3 files changed, 52 insertions(+), 51 deletions(-) diff --git a/src/shims/env.rs b/src/shims/env.rs index 402e267088..298fefdb0f 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -178,7 +178,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &var, buf_ptr, buf_size.into(), - /*truncate*/ false, )?)) // This can in fact return 0. It is up to the caller to set last_error to 0 // beforehand and check it afterwards to exclude that case. @@ -380,7 +379,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This can in fact return 0. It is up to the caller to set last_error to 0 // beforehand and check it afterwards to exclude that case. return Ok(Scalar::from_u32(windows_check_buffer_size( - this.write_path_to_wide_str(&cwd, buf, size, /*truncate*/ false)?, + this.write_path_to_wide_str(&cwd, buf, size)?, ))); } Err(e) => this.set_last_error_from_io_error(e.kind())?, @@ -535,12 +534,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // Of course we cannot use `windows_check_buffer_size` here since this uses // a different method for dealing with a too-small buffer than the other functions... - let (success, len) = this.write_path_to_wide_str( - home, - buf, - size_avail.into(), - /*truncate*/ false, - )?; + let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?; // The Windows docs just say that this is written on failure. But std // seems to rely on it always being written. this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?; diff --git a/src/shims/os_str.rs b/src/shims/os_str.rs index 3e8c35d48a..5fcea9ced6 100644 --- a/src/shims/os_str.rs +++ b/src/shims/os_str.rs @@ -72,11 +72,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { u16vec_to_osstring(u16_vec) } - /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what - /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying - /// to write if `size` is not large enough to fit the contents of `os_string` plus a null - /// terminator. It returns `Ok((true, length))` if the writing process was successful. The - /// string length returned does include the null terminator. + /// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what the + /// Unix APIs usually handle. Returns `(success, full_len)`, where length includes the null + /// terminator. On failure, nothing is written. fn write_os_str_to_c_str( &mut self, os_str: &OsStr, @@ -87,19 +85,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { self.eval_context_mut().write_c_str(bytes, ptr, size) } - /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the - /// Windows APIs usually handle. - /// - /// If `truncate == false` (the usual mode of operation), this function returns `Ok((false, - /// length))` without trying to write if `size` is not large enough to fit the contents of - /// `os_string` plus a null terminator. It returns `Ok((true, length))` if the writing process - /// was successful. The string length returned does include the null terminator. Length is - /// measured in units of `u16.` - /// - /// If `truncate == true`, then in case `size` is not large enough it *will* write the first - /// `size.saturating_sub(1)` many items, followed by a null terminator (if `size > 0`). - /// The return value is still `(false, length)` in that case. - fn write_os_str_to_wide_str( + /// Internal helper to share code between `write_os_str_to_wide_str` and + /// `write_os_str_to_wide_str_truncated`. + fn write_os_str_to_wide_str_helper( &mut self, os_str: &OsStr, ptr: Pointer>, @@ -133,6 +121,29 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((written, size_needed)) } + /// Helper function to write an OsStr as a 0x0000-terminated u16-sequence, which is what the + /// Windows APIs usually handle. Returns `(success, full_len)`, where length is measured + /// in units of `u16` and includes the null terminator. On failure, nothing is written. + fn write_os_str_to_wide_str( + &mut self, + os_str: &OsStr, + ptr: Pointer>, + size: u64, + ) -> InterpResult<'tcx, (bool, u64)> { + self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ false) + } + + /// Like `write_os_str_to_wide_str`, but on failure as much as possible is written into + /// the buffer (always with a null terminator). + fn write_os_str_to_wide_str_truncated( + &mut self, + os_str: &OsStr, + ptr: Pointer>, + size: u64, + ) -> InterpResult<'tcx, (bool, u64)> { + self.write_os_str_to_wide_str_helper(os_str, ptr, size, /*truncate*/ true) + } + /// Allocate enough memory to store the given `OsStr` as a null-terminated sequence of bytes. fn alloc_os_str_as_c_str( &mut self, @@ -160,9 +171,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; - let (written, _) = self - .write_os_str_to_wide_str(os_str, arg_place.ptr(), size, /*truncate*/ false) - .unwrap(); + let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr(), size).unwrap(); assert!(written); Ok(arg_place.ptr()) } @@ -217,12 +226,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { path: &Path, ptr: Pointer>, size: u64, - truncate: bool, ) -> InterpResult<'tcx, (bool, u64)> { let this = self.eval_context_mut(); let os_str = this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); - this.write_os_str_to_wide_str(&os_str, ptr, size, truncate) + this.write_os_str_to_wide_str(&os_str, ptr, size) + } + + /// Write a Path to the machine memory (as a null-terminated sequence of `u16`s), + /// adjusting path separators if needed. + fn write_path_to_wide_str_truncated( + &mut self, + path: &Path, + ptr: Pointer>, + size: u64, + ) -> InterpResult<'tcx, (bool, u64)> { + let this = self.eval_context_mut(); + let os_str = + this.convert_path(Cow::Borrowed(path.as_os_str()), PathConversion::HostToTarget); + this.write_os_str_to_wide_str_truncated(&os_str, ptr, size) } /// Allocate enough memory to store a Path as a null-terminated sequence of bytes, diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index e0cca43d26..24f7cd18e7 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -232,12 +232,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } Ok(abs_filename) => { Scalar::from_u32(helpers::windows_check_buffer_size( - this.write_path_to_wide_str( - &abs_filename, - buffer, - size.into(), - /*truncate*/ false, - )?, + this.write_path_to_wide_str(&abs_filename, buffer, size.into())?, )) // This can in fact return 0. It is up to the caller to set last_error to 0 // beforehand and check it afterwards to exclude that case. @@ -608,15 +603,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Using the host current_exe is a bit off, but consistent with Linux // (where stdlib reads /proc/self/exe). - // Unfortunately this Windows function has a crazy behavior so we can't just use - // `write_path_to_wide_str`... let path = std::env::current_exe().unwrap(); - let (all_written, size_needed) = this.write_path_to_wide_str( - &path, - filename, - size.into(), - /*truncate*/ true, - )?; + let (all_written, size_needed) = + this.write_path_to_wide_str_truncated(&path, filename, size.into())?; if all_written { // If the function succeeds, the return value is the length of the string that @@ -656,12 +645,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Some(err) => format!("{err}"), None => format!(""), }; - let (complete, length) = this.write_os_str_to_wide_str( - OsStr::new(&formatted), - buffer, - size.into(), - /*truncate*/ false, - )?; + let (complete, length) = + this.write_os_str_to_wide_str(OsStr::new(&formatted), buffer, size.into())?; if !complete { // The API docs don't say what happens when the buffer is not big enough... // Let's just bail. From 10e8bb87fe416bc190426789032594aa1aa311f2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 10:01:00 +0200 Subject: [PATCH 087/208] avoid some unnecessary Scalar-i32-Scalar roundtrips --- src/shims/unix/linux/mem.rs | 4 ++-- src/shims/unix/mem.rs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/shims/unix/linux/mem.rs b/src/shims/unix/linux/mem.rs index ec2922d027..3948216f72 100644 --- a/src/shims/unix/linux/mem.rs +++ b/src/shims/unix/linux/mem.rs @@ -23,7 +23,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // old_address must be a multiple of the page size #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); } @@ -37,7 +37,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if flags & this.eval_libc_i32("MREMAP_MAYMOVE") == 0 { // We only support MREMAP_MAYMOVE, so not passing the flag is just a failure - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); } diff --git a/src/shims/unix/mem.rs b/src/shims/unix/mem.rs index d3470893db..f52dc23656 100644 --- a/src/shims/unix/mem.rs +++ b/src/shims/unix/mem.rs @@ -53,11 +53,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // First, we do some basic argument validation as required by mmap if (flags & (map_private | map_shared)).count_ones() != 1 { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); } if length == 0 { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); } @@ -77,7 +77,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // // Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE. if flags & map_fixed != 0 || prot != prot_read | prot_write { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("ENOTSUP")))?; + this.set_last_error(this.eval_libc("ENOTSUP"))?; return Ok(this.eval_libc("MAP_FAILED")); } @@ -96,11 +96,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let align = this.machine.page_align(); let Some(map_length) = length.checked_next_multiple_of(this.machine.page_size) else { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); }; if map_length > this.target_usize_max() { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); } @@ -131,16 +131,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // as a dealloc. #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero if addr.addr().bytes() % this.machine.page_size != 0 { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(Scalar::from_i32(-1)); } let Some(length) = length.checked_next_multiple_of(this.machine.page_size) else { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(Scalar::from_i32(-1)); }; if length > this.target_usize_max() { - this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; + this.set_last_error(this.eval_libc("EINVAL"))?; return Ok(this.eval_libc("MAP_FAILED")); } From 192a4db2cf5b0bbfc603faee200d2cdbbcc8b459 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Apr 2024 09:10:16 +0200 Subject: [PATCH 088/208] avoid 'let _' in tests where we actually want the value to be computed --- tests/fail/both_borrows/aliasing_mut4.rs | 2 +- .../storage_dead_dangling.rs | 2 +- .../storage_dead_dangling.stderr | 4 ++-- tests/pass/adjacent-allocs.rs | 2 +- tests/pass/dyn-upcast.rs | 20 +++++++++---------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/fail/both_borrows/aliasing_mut4.rs b/tests/fail/both_borrows/aliasing_mut4.rs index e188a1f0c3..c656a50964 100644 --- a/tests/fail/both_borrows/aliasing_mut4.rs +++ b/tests/fail/both_borrows/aliasing_mut4.rs @@ -8,7 +8,7 @@ use std::mem; pub fn safe(x: &i32, y: &mut Cell) { //~[stack]^ ERROR: protect y.set(1); - let _ = *x; + let _load = *x; } fn main() { diff --git a/tests/fail/dangling_pointers/storage_dead_dangling.rs b/tests/fail/dangling_pointers/storage_dead_dangling.rs index f9983f48c6..f434928680 100644 --- a/tests/fail/dangling_pointers/storage_dead_dangling.rs +++ b/tests/fail/dangling_pointers/storage_dead_dangling.rs @@ -10,7 +10,7 @@ fn fill(v: &mut i32) { } fn evil() { - let _ = unsafe { &mut *(LEAK as *mut i32) }; //~ ERROR: is a dangling pointer + let _ref = unsafe { &mut *(LEAK as *mut i32) }; //~ ERROR: is a dangling pointer } fn main() { diff --git a/tests/fail/dangling_pointers/storage_dead_dangling.stderr b/tests/fail/dangling_pointers/storage_dead_dangling.stderr index 27e5a86506..73c3ff1ee0 100644 --- a/tests/fail/dangling_pointers/storage_dead_dangling.stderr +++ b/tests/fail/dangling_pointers/storage_dead_dangling.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance) --> $DIR/storage_dead_dangling.rs:LL:CC | -LL | let _ = unsafe { &mut *(LEAK as *mut i32) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance) +LL | let _ref = unsafe { &mut *(LEAK as *mut i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: $HEX[noalloc] is a dangling pointer (it has no provenance) | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/pass/adjacent-allocs.rs b/tests/pass/adjacent-allocs.rs index cbf41d68b5..8be4bdac7e 100644 --- a/tests/pass/adjacent-allocs.rs +++ b/tests/pass/adjacent-allocs.rs @@ -30,7 +30,7 @@ fn test1() { // See https://github.com/rust-lang/miri/issues/1866#issuecomment-985770125 { let m = 0u64; - let _ = &m as *const u64; + let _ptr = &m as *const u64; } let iptr = ptr as usize; diff --git a/tests/pass/dyn-upcast.rs b/tests/pass/dyn-upcast.rs index ddc4bdcf08..ff995f3819 100644 --- a/tests/pass/dyn-upcast.rs +++ b/tests/pass/dyn-upcast.rs @@ -69,7 +69,7 @@ fn basic() { } let baz: &dyn Baz = &1; - let _: &dyn fmt::Debug = baz; + let _up: &dyn fmt::Debug = baz; assert_eq!(*baz, 1); assert_eq!(baz.a(), 100); assert_eq!(baz.b(), 200); @@ -79,7 +79,7 @@ fn basic() { assert_eq!(baz.w(), 21); let bar: &dyn Bar = baz; - let _: &dyn fmt::Debug = bar; + let _up: &dyn fmt::Debug = bar; assert_eq!(*bar, 1); assert_eq!(bar.a(), 100); assert_eq!(bar.b(), 200); @@ -88,14 +88,14 @@ fn basic() { assert_eq!(bar.w(), 21); let foo: &dyn Foo = baz; - let _: &dyn fmt::Debug = foo; + let _up: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); assert_eq!(foo.z(), 11); assert_eq!(foo.y(), 12); let foo: &dyn Foo = bar; - let _: &dyn fmt::Debug = foo; + let _up: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); assert_eq!(foo.z(), 11); @@ -168,7 +168,7 @@ fn diamond() { } let baz: &dyn Baz = &1; - let _: &dyn fmt::Debug = baz; + let _up: &dyn fmt::Debug = baz; assert_eq!(*baz, 1); assert_eq!(baz.a(), 100); assert_eq!(baz.b(), 200); @@ -180,7 +180,7 @@ fn diamond() { assert_eq!(baz.v(), 31); let bar1: &dyn Bar1 = baz; - let _: &dyn fmt::Debug = bar1; + let _up: &dyn fmt::Debug = bar1; assert_eq!(*bar1, 1); assert_eq!(bar1.a(), 100); assert_eq!(bar1.b(), 200); @@ -189,7 +189,7 @@ fn diamond() { assert_eq!(bar1.w(), 21); let bar2: &dyn Bar2 = baz; - let _: &dyn fmt::Debug = bar2; + let _up: &dyn fmt::Debug = bar2; assert_eq!(*bar2, 1); assert_eq!(bar2.a(), 100); assert_eq!(bar2.c(), 300); @@ -198,17 +198,17 @@ fn diamond() { assert_eq!(bar2.v(), 31); let foo: &dyn Foo = baz; - let _: &dyn fmt::Debug = foo; + let _up: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); let foo: &dyn Foo = bar1; - let _: &dyn fmt::Debug = foo; + let _up: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); let foo: &dyn Foo = bar2; - let _: &dyn fmt::Debug = foo; + let _up: &dyn fmt::Debug = foo; assert_eq!(*foo, 1); assert_eq!(foo.a(), 100); } From e84df5bd708f2457276308b653fa62d7901cb174 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 11 Apr 2024 13:15:34 +0000 Subject: [PATCH 089/208] Error on using `yield` without also using `#[coroutine]` on the closure And suggest adding the `#[coroutine]` to the closure --- tests/fail/coroutine-pinned-moved.rs | 4 +-- tests/pass/coroutine.rs | 36 +++++++++---------- .../coroutine-self-referential.rs | 4 +-- tests/pass/track-caller-attribute.rs | 6 ++-- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/fail/coroutine-pinned-moved.rs b/tests/fail/coroutine-pinned-moved.rs index 005ae7e913..8648be2a26 100644 --- a/tests/fail/coroutine-pinned-moved.rs +++ b/tests/fail/coroutine-pinned-moved.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-validation -Zmiri-disable-stacked-borrows -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::{ ops::{Coroutine, CoroutineState}, @@ -7,7 +7,7 @@ use std::{ }; fn firstn() -> impl Coroutine { - static move || { + #[coroutine] static move || { let mut num = 0; let num = &mut num; *num += 0; diff --git a/tests/pass/coroutine.rs b/tests/pass/coroutine.rs index 7e1f64df04..e76abfc418 100644 --- a/tests/pass/coroutine.rs +++ b/tests/pass/coroutine.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -#![feature(coroutines, coroutine_trait, never_type)] +#![feature(coroutines, coroutine_trait, never_type, stmt_expr_attributes)] use std::fmt::Debug; use std::mem::ManuallyDrop; @@ -43,9 +43,9 @@ fn basic() { panic!() } - finish(1, false, || yield 1); + finish(1, false, #[coroutine] || yield 1); - finish(3, false, || { + finish(3, false, #[coroutine] || { let mut x = 0; yield 1; x += 1; @@ -55,27 +55,27 @@ fn basic() { assert_eq!(x, 2); }); - finish(7 * 8 / 2, false, || { + finish(7 * 8 / 2, false, #[coroutine] || { for i in 0..8 { yield i; } }); - finish(1, false, || { + finish(1, false, #[coroutine] || { if true { yield 1; } else { } }); - finish(1, false, || { + finish(1, false, #[coroutine] || { if false { } else { yield 1; } }); - finish(2, false, || { + finish(2, false, #[coroutine] || { if { yield 1; false @@ -88,7 +88,7 @@ fn basic() { // also test self-referential coroutines assert_eq!( - finish(5, true, static || { + finish(5, true, #[coroutine] static || { let mut x = 5; let y = &mut x; *y = 5; @@ -99,7 +99,7 @@ fn basic() { 10 ); assert_eq!( - finish(5, true, || { + finish(5, true, #[coroutine] || { let mut x = Box::new(5); let y = &mut *x; *y = 5; @@ -111,7 +111,7 @@ fn basic() { ); let b = true; - finish(1, false, || { + finish(1, false, #[coroutine] || { yield 1; if b { return; @@ -123,7 +123,7 @@ fn basic() { drop(x); }); - finish(3, false, || { + finish(3, false, #[coroutine] || { yield 1; #[allow(unreachable_code)] let _x: (String, !) = (String::new(), { @@ -172,7 +172,7 @@ fn smoke_resume_arg() { } drain( - &mut |mut b| { + &mut #[coroutine] |mut b| { while b != 0 { b = yield (b + 1); } @@ -181,21 +181,21 @@ fn smoke_resume_arg() { vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))], ); - expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))])); + expect_drops(2, || drain(&mut #[coroutine] |a| yield a, vec![(DropMe, Yielded(DropMe))])); expect_drops(6, || { drain( - &mut |a| yield yield a, + &mut #[coroutine] |a| yield yield a, vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))], ) }); #[allow(unreachable_code)] - expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))])); + expect_drops(2, || drain(&mut #[coroutine] |a| yield return a, vec![(DropMe, Complete(DropMe))])); expect_drops(2, || { drain( - &mut |a: DropMe| { + &mut #[coroutine] |a: DropMe| { if false { yield () } else { a } }, vec![(DropMe, Complete(DropMe))], @@ -205,7 +205,7 @@ fn smoke_resume_arg() { expect_drops(4, || { drain( #[allow(unused_assignments, unused_variables)] - &mut |mut a: DropMe| { + &mut #[coroutine] |mut a: DropMe| { a = yield; a = yield; a = yield; @@ -228,7 +228,7 @@ fn uninit_fields() { } fn run(x: bool, y: bool) { - let mut c = || { + let mut c = #[coroutine] || { if x { let _a: T; if y { diff --git a/tests/pass/stacked-borrows/coroutine-self-referential.rs b/tests/pass/stacked-borrows/coroutine-self-referential.rs index c4b15c8758..bb98e024a0 100644 --- a/tests/pass/stacked-borrows/coroutine-self-referential.rs +++ b/tests/pass/stacked-borrows/coroutine-self-referential.rs @@ -1,6 +1,6 @@ // See https://github.com/rust-lang/unsafe-code-guidelines/issues/148: // this fails when Stacked Borrows is strictly applied even to `!Unpin` types. -#![feature(coroutines, coroutine_trait)] +#![feature(coroutines, coroutine_trait, stmt_expr_attributes)] use std::{ ops::{Coroutine, CoroutineState}, @@ -8,7 +8,7 @@ use std::{ }; fn firstn() -> impl Coroutine { - static move || { + #[coroutine] static move || { let mut num = 0; let num = &mut num; diff --git a/tests/pass/track-caller-attribute.rs b/tests/pass/track-caller-attribute.rs index d88bcc9885..c3803af3cc 100644 --- a/tests/pass/track-caller-attribute.rs +++ b/tests/pass/track-caller-attribute.rs @@ -232,7 +232,7 @@ fn test_coroutine() { } #[rustfmt::skip] - let coroutine = #[track_caller] |arg: String| { + let coroutine = #[track_caller] #[coroutine] |arg: String| { yield ("first", arg.clone(), Location::caller()); yield ("second", arg.clone(), Location::caller()); }; @@ -255,7 +255,7 @@ fn test_coroutine() { assert_eq!(mono_loc.column(), 42); #[rustfmt::skip] - let non_tracked_coroutine = || { yield Location::caller(); }; + let non_tracked_coroutine = #[coroutine] || { yield Location::caller(); }; let non_tracked_line = line!() - 1; // This is the line of the coroutine, not its caller let non_tracked_loc = match Box::pin(non_tracked_coroutine).as_mut().resume(()) { CoroutineState::Yielded(val) => val, @@ -263,7 +263,7 @@ fn test_coroutine() { }; assert_eq!(non_tracked_loc.file(), file!()); assert_eq!(non_tracked_loc.line(), non_tracked_line); - assert_eq!(non_tracked_loc.column(), 44); + assert_eq!(non_tracked_loc.column(), 57); } fn main() { From d34d5957ff558f4cebd775b32e9d7ee4b0de9d4d Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Sat, 20 Apr 2024 22:28:02 +0100 Subject: [PATCH 090/208] Fix miri test --- tests/pass/portable-simd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pass/portable-simd.rs b/tests/pass/portable-simd.rs index cdb441b450..1fc713d48d 100644 --- a/tests/pass/portable-simd.rs +++ b/tests/pass/portable-simd.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-strict-provenance -#![feature(portable_simd, adt_const_params, inline_const, core_intrinsics)] +#![feature(portable_simd, adt_const_params, core_intrinsics)] #![allow(incomplete_features, internal_features)] use std::intrinsics::simd as intrinsics; use std::ptr; From fb9bd72021e6d9ade212ef40dbba6747480b2615 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Apr 2024 14:58:12 +0200 Subject: [PATCH 091/208] unix_sigpipe: don't inline DEFAULT, just use it from rustc --- src/eval.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index d74cd5ff3e..2242768a56 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -386,10 +386,9 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let main_ptr = ecx.fn_ptr(FnVal::Instance(entry_instance)); - // Inlining of `DEFAULT` from - // https://github.com/rust-lang/rust/blob/master/compiler/rustc_session/src/config/sigpipe.rs. // Always using DEFAULT is okay since we don't support signals in Miri anyway. - let sigpipe = 2; + // (This means we are effectively ignoring `#[unix_sigpipe]`.) + let sigpipe = rustc_session::config::sigpipe::DEFAULT; ecx.call_function( start_instance, From 0b5a4447bf5baae83218d39e06725786959a4fae Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 16 Apr 2024 12:51:57 +0000 Subject: [PATCH 092/208] Stabilize `std::path::absolute` --- src/lib.rs | 1 - tests/pass/shims/path.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e1c0da9118..44727e01ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,6 @@ #![feature(let_chains)] #![feature(lint_reasons)] #![feature(trait_upcasting)] -#![feature(absolute_path)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/tests/pass/shims/path.rs b/tests/pass/shims/path.rs index 9fc6e7faef..cadbeb476b 100644 --- a/tests/pass/shims/path.rs +++ b/tests/pass/shims/path.rs @@ -1,5 +1,4 @@ //@compile-flags: -Zmiri-disable-isolation -#![feature(absolute_path)] use std::path::{absolute, Path}; #[track_caller] From 743ab271d7e659ce6b176a1087d8e4849c412460 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> Date: Wed, 24 Apr 2024 10:57:18 -0400 Subject: [PATCH 093/208] Upgrade to `actions/checkout@v4` in `ci.yml`. This is a newer version of the same action. None of the uses here were particularly special (no complex features of v3 were used) so this is a straightforward as-is upgrade. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0dab9f509..73afd2a12a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: env: HOST_TARGET: ${{ matrix.host_target }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Show Rust version (stable toolchain) run: | @@ -85,7 +85,7 @@ jobs: name: style checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # This is exactly duplicated from above. GHA is pretty terrible when it comes # to avoiding code duplication. @@ -191,7 +191,7 @@ jobs: The Miri Cronjobs Bot' # Attempt to auto-sync with rustc - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 256 # get a bit more of the history - name: install josh-proxy From 4af9665e49a741bc045442ba149e7823db813b21 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 24 Apr 2024 18:05:03 +0200 Subject: [PATCH 094/208] make miri-script a workspace root --- miri-script/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/miri-script/Cargo.toml b/miri-script/Cargo.toml index aaa788d584..79d0b13600 100644 --- a/miri-script/Cargo.toml +++ b/miri-script/Cargo.toml @@ -8,7 +8,9 @@ version = "0.1.0" default-run = "miri-script" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +# We make this a workspace root so that cargo does not go looking in ../Cargo.toml for the workspace root. +# This is needed to make this package build on stable when the parent package uses unstable cargo features. [dependencies] which = "4.4" From b1060bbf93779399387c2f84534cd80b8a5ec8bf Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 25 Apr 2024 04:57:07 +0000 Subject: [PATCH 095/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 3191355ccb..a6433a8e28 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -c1feb3eceef7d5f0126c309a87062cf413fe0a25 +cb3752d20e0f5d24348062211102a08d46fbecff From 15b04db187119572fc7f020eb8f628b970998df6 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 25 Apr 2024 05:05:41 +0000 Subject: [PATCH 096/208] fmt --- tests/fail/coroutine-pinned-moved.rs | 3 +- tests/pass/coroutine.rs | 223 ++++++++++++------ .../coroutine-self-referential.rs | 3 +- 3 files changed, 149 insertions(+), 80 deletions(-) diff --git a/tests/fail/coroutine-pinned-moved.rs b/tests/fail/coroutine-pinned-moved.rs index 8648be2a26..46ec58810a 100644 --- a/tests/fail/coroutine-pinned-moved.rs +++ b/tests/fail/coroutine-pinned-moved.rs @@ -7,7 +7,8 @@ use std::{ }; fn firstn() -> impl Coroutine { - #[coroutine] static move || { + #[coroutine] + static move || { let mut num = 0; let num = &mut num; *num += 0; diff --git a/tests/pass/coroutine.rs b/tests/pass/coroutine.rs index e76abfc418..7822c136d9 100644 --- a/tests/pass/coroutine.rs +++ b/tests/pass/coroutine.rs @@ -43,94 +43,144 @@ fn basic() { panic!() } - finish(1, false, #[coroutine] || yield 1); + finish( + 1, + false, + #[coroutine] + || yield 1, + ); - finish(3, false, #[coroutine] || { - let mut x = 0; - yield 1; - x += 1; - yield 1; - x += 1; - yield 1; - assert_eq!(x, 2); - }); + finish( + 3, + false, + #[coroutine] + || { + let mut x = 0; + yield 1; + x += 1; + yield 1; + x += 1; + yield 1; + assert_eq!(x, 2); + }, + ); - finish(7 * 8 / 2, false, #[coroutine] || { - for i in 0..8 { - yield i; - } - }); + finish( + 7 * 8 / 2, + false, + #[coroutine] + || { + for i in 0..8 { + yield i; + } + }, + ); - finish(1, false, #[coroutine] || { - if true { - yield 1; - } else { - } - }); + finish( + 1, + false, + #[coroutine] + || { + if true { + yield 1; + } else { + } + }, + ); - finish(1, false, #[coroutine] || { - if false { - } else { - yield 1; - } - }); + finish( + 1, + false, + #[coroutine] + || { + if false { + } else { + yield 1; + } + }, + ); - finish(2, false, #[coroutine] || { - if { - yield 1; - false - } { + finish( + 2, + false, + #[coroutine] + || { + if { + yield 1; + false + } { + yield 1; + panic!() + } yield 1; - panic!() - } - yield 1; - }); + }, + ); // also test self-referential coroutines assert_eq!( - finish(5, true, #[coroutine] static || { - let mut x = 5; - let y = &mut x; - *y = 5; - yield *y; - *y = 10; - x - }), + finish( + 5, + true, + #[coroutine] + static || { + let mut x = 5; + let y = &mut x; + *y = 5; + yield *y; + *y = 10; + x + } + ), 10 ); assert_eq!( - finish(5, true, #[coroutine] || { - let mut x = Box::new(5); - let y = &mut *x; - *y = 5; - yield *y; - *y = 10; - *x - }), + finish( + 5, + true, + #[coroutine] + || { + let mut x = Box::new(5); + let y = &mut *x; + *y = 5; + yield *y; + *y = 10; + *x + } + ), 10 ); let b = true; - finish(1, false, #[coroutine] || { - yield 1; - if b { - return; - } - #[allow(unused)] - let x = never(); - #[allow(unreachable_code)] - yield 2; - drop(x); - }); - - finish(3, false, #[coroutine] || { - yield 1; - #[allow(unreachable_code)] - let _x: (String, !) = (String::new(), { + finish( + 1, + false, + #[coroutine] + || { + yield 1; + if b { + return; + } + #[allow(unused)] + let x = never(); + #[allow(unreachable_code)] yield 2; - return; - }); - }); + drop(x); + }, + ); + + finish( + 3, + false, + #[coroutine] + || { + yield 1; + #[allow(unreachable_code)] + let _x: (String, !) = (String::new(), { + yield 2; + return; + }); + }, + ); } fn smoke_resume_arg() { @@ -172,7 +222,8 @@ fn smoke_resume_arg() { } drain( - &mut #[coroutine] |mut b| { + &mut #[coroutine] + |mut b| { while b != 0 { b = yield (b + 1); } @@ -181,21 +232,35 @@ fn smoke_resume_arg() { vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))], ); - expect_drops(2, || drain(&mut #[coroutine] |a| yield a, vec![(DropMe, Yielded(DropMe))])); + expect_drops(2, || { + drain( + &mut #[coroutine] + |a| yield a, + vec![(DropMe, Yielded(DropMe))], + ) + }); expect_drops(6, || { drain( - &mut #[coroutine] |a| yield yield a, + &mut #[coroutine] + |a| yield yield a, vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))], ) }); #[allow(unreachable_code)] - expect_drops(2, || drain(&mut #[coroutine] |a| yield return a, vec![(DropMe, Complete(DropMe))])); + expect_drops(2, || { + drain( + &mut #[coroutine] + |a| yield return a, + vec![(DropMe, Complete(DropMe))], + ) + }); expect_drops(2, || { drain( - &mut #[coroutine] |a: DropMe| { + &mut #[coroutine] + |a: DropMe| { if false { yield () } else { a } }, vec![(DropMe, Complete(DropMe))], @@ -205,7 +270,8 @@ fn smoke_resume_arg() { expect_drops(4, || { drain( #[allow(unused_assignments, unused_variables)] - &mut #[coroutine] |mut a: DropMe| { + &mut #[coroutine] + |mut a: DropMe| { a = yield; a = yield; a = yield; @@ -228,7 +294,8 @@ fn uninit_fields() { } fn run(x: bool, y: bool) { - let mut c = #[coroutine] || { + let mut c = #[coroutine] + || { if x { let _a: T; if y { diff --git a/tests/pass/stacked-borrows/coroutine-self-referential.rs b/tests/pass/stacked-borrows/coroutine-self-referential.rs index bb98e024a0..259fc72d39 100644 --- a/tests/pass/stacked-borrows/coroutine-self-referential.rs +++ b/tests/pass/stacked-borrows/coroutine-self-referential.rs @@ -8,7 +8,8 @@ use std::{ }; fn firstn() -> impl Coroutine { - #[coroutine] static move || { + #[coroutine] + static move || { let mut num = 0; let num = &mut num; From d47e4631668e5528328d7abb878f9bc9c0ad9b5f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 08:42:52 +0200 Subject: [PATCH 097/208] weak memory outdated loads: show where the load was from --- src/concurrency/weak_memory.rs | 4 +++- src/diagnostics.rs | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/concurrency/weak_memory.rs b/src/concurrency/weak_memory.rs index f544393cfe..574962c48d 100644 --- a/src/concurrency/weak_memory.rs +++ b/src/concurrency/weak_memory.rs @@ -520,7 +520,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: validate, )?; if global.track_outdated_loads && recency == LoadRecency::Outdated { - this.emit_diagnostic(NonHaltingDiagnostic::WeakMemoryOutdatedLoad); + this.emit_diagnostic(NonHaltingDiagnostic::WeakMemoryOutdatedLoad { + ptr: place.ptr(), + }); } return Ok(loaded); diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 0c0ac4c603..9fa786332e 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -125,7 +125,9 @@ pub enum NonHaltingDiagnostic { Int2Ptr { details: bool, }, - WeakMemoryOutdatedLoad, + WeakMemoryOutdatedLoad { + ptr: Pointer>, + }, } /// Level of Miri specific diagnostics @@ -583,7 +585,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { | AccessedAlloc(..) | FreedAlloc(..) | ProgressReport { .. } - | WeakMemoryOutdatedLoad => ("tracking was triggered".to_string(), DiagLevel::Note), + | WeakMemoryOutdatedLoad { .. } => + ("tracking was triggered".to_string(), DiagLevel::Note), }; let msg = match &e { @@ -610,8 +613,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ProgressReport { .. } => format!("progress report: current operation being executed is here"), Int2Ptr { .. } => format!("integer-to-pointer cast"), - WeakMemoryOutdatedLoad => - format!("weak memory emulation: outdated value returned from load"), + WeakMemoryOutdatedLoad { ptr } => + format!("weak memory emulation: outdated value returned from load at {ptr}"), }; let notes = match &e { From 95057c4d4012ce7c3e005321dc3df24d01f9e1f9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 08:43:06 +0200 Subject: [PATCH 098/208] add a test for the TLS memory leak --- tests/many-seeds/tls-leak.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/many-seeds/tls-leak.rs diff --git a/tests/many-seeds/tls-leak.rs b/tests/many-seeds/tls-leak.rs new file mode 100644 index 0000000000..70a506958d --- /dev/null +++ b/tests/many-seeds/tls-leak.rs @@ -0,0 +1,13 @@ +//! Regression test for . +use std::thread; + +pub(crate) fn with_thread_local() { + thread_local! { static X: Box = Box::new(0); } + X.with(|_x| {}) +} + +fn main() { + let j2 = thread::spawn(with_thread_local); + with_thread_local(); + j2.join().unwrap(); +} From 10f34a66d3d6e642665b0dc807d0dc378fdf177c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 23 Apr 2024 08:44:49 +0200 Subject: [PATCH 099/208] run many-seeds tests at least a few times on all tier 1 targets --- ci/ci.sh | 18 +++++++++++------- tests/many-seeds/tls-leak.rs | 21 +++++++++++++++++---- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index f8ba612750..ad0c795315 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -128,16 +128,18 @@ function run_tests_minimal { ## Main Testing Logic ## # In particular, fully cover all tier 1 targets. +# We also want to run the many-seeds tests on all tier 1 targets. case $HOST_TARGET in x86_64-unknown-linux-gnu) # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests - MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests - MIRI_TEST_TARGET=x86_64-apple-darwin run_tests - MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests + # With reduced many-seed count to avoid spending too much time on that. + # (All OSes are run with 64 seeds at least once though via the macOS runner.) + MANY_SEEDS=16 MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests + MANY_SEEDS=16 MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests + MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-apple-darwin run_tests + MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests # Extra tier 2 MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests @@ -155,13 +157,15 @@ case $HOST_TARGET in # Host (tier 2) GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests + MANY_SEEDS=64 MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests + MANY_SEEDS=64 MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests # Extra tier 2 MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture ;; i686-pc-windows-msvc) # Host - # Only smoke-test `many-seeds`; 64 runs take 15min here! + # Only smoke-test `many-seeds`; 64 runs of just the scoped-thread-leak test take 15min here! + # See . GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=1 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, diff --git a/tests/many-seeds/tls-leak.rs b/tests/many-seeds/tls-leak.rs index 70a506958d..3b24363343 100644 --- a/tests/many-seeds/tls-leak.rs +++ b/tests/many-seeds/tls-leak.rs @@ -1,13 +1,26 @@ //! Regression test for . use std::thread; -pub(crate) fn with_thread_local() { +fn with_thread_local1() { thread_local! { static X: Box = Box::new(0); } X.with(|_x| {}) } +fn with_thread_local2() { + thread_local! { static Y: Box = Box::new(0); } + Y.with(|_y| {}) +} + fn main() { - let j2 = thread::spawn(with_thread_local); - with_thread_local(); - j2.join().unwrap(); + // Here we have two threads racing on initializing the thread-local and adding it to the global + // dtor list (on targets that have such a list, i.e., targets without target_thread_local). + let t = thread::spawn(with_thread_local1); + with_thread_local1(); + t.join().unwrap(); + + // Here we have one thread running the destructors racing with another thread initializing a + // thread-local. The second thread adds a destructor that could be picked up by the first. + let t = thread::spawn(|| { /* immediately just run destructors */ }); + with_thread_local2(); // initialize thread-local + t.join().unwrap(); } From 4bbd098f5032cb75f7799d6f3183e764f284fe7c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 25 Apr 2024 08:13:34 +0200 Subject: [PATCH 100/208] CI: run benches with hyperfine rather than bash --- .github/workflows/ci.yml | 8 ++++---- ci/ci.sh | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73afd2a12a..69442295b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,12 +57,12 @@ jobs: ~/.cargo/bin ~/.cargo/.crates.toml ~/.cargo/.crates2.json - key: cargo-${{ runner.os }}-reset20240331-${{ hashFiles('**/Cargo.lock') }} - restore-keys: cargo-${{ runner.os }}-reset20240331 + key: cargo-${{ runner.os }}-reset20240425-${{ hashFiles('**/Cargo.lock') }} + restore-keys: cargo-${{ runner.os }}-reset20240425 - - name: Install rustup-toolchain-install-master + - name: Install tools if: ${{ steps.cache.outputs.cache-hit != 'true' }} - run: cargo install -f rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master hyperfine - name: Install miri toolchain run: | diff --git a/ci/ci.sh b/ci/ci.sh index f8ba612750..eb32f325a1 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -78,8 +78,8 @@ function run_tests { done fi if [ -n "${TEST_BENCH-}" ]; then - # Check that the benchmarks build and run, but without actually benchmarking. - time HYPERFINE="'$BASH' -c" ./miri bench + # Check that the benchmarks build and run, but only once. + time HYPERFINE="hyperfine -w0 -r1" ./miri bench fi ## test-cargo-miri From b57428c08b1209c74bd111a4e0d2b2fdf8db7942 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 9 Feb 2024 15:39:25 +0300 Subject: [PATCH 101/208] debuginfo: Stabilize `-Z debug-macros`, `-Z collapse-macro-debuginfo` and `#[collapse_debuginfo]` `-Z debug-macros` is "stabilized" by enabling it by default and removing. `-Z collapse-macro-debuginfo` is stabilized as `-C collapse-macro-debuginfo`. It now supports all typical boolean values (`parse_opt_bool`) in addition to just yes/no. Default value of `collapse_debuginfo` was changed from `false` to `external` (i.e. collapsed if external, not collapsed if local). `#[collapse_debuginfo]` attribute without a value is no longer supported to avoid guessing the default. --- src/shims/backtrace.rs | 10 +++------- tests/pass/backtrace/backtrace-api-v0.stdout | 2 +- tests/pass/backtrace/backtrace-api-v1.stdout | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/shims/backtrace.rs b/src/shims/backtrace.rs index abfa7143a7..294ad50c33 100644 --- a/src/shims/backtrace.rs +++ b/src/shims/backtrace.rs @@ -2,7 +2,7 @@ use crate::*; use rustc_ast::ast::Mutability; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{self, Instance, Ty}; -use rustc_span::{BytePos, Loc, Symbol}; +use rustc_span::{hygiene, BytePos, Loc, Symbol}; use rustc_target::{abi::Size, spec::abi::Abi}; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -45,12 +45,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let mut data = Vec::new(); for frame in this.active_thread_stack().iter().rev() { - let mut span = frame.current_span(); - // Match the behavior of runtime backtrace spans - // by using a non-macro span in our backtrace. See `FunctionCx::debug_loc`. - if span.from_expansion() && !tcx.sess.opts.unstable_opts.debug_macros { - span = rustc_span::hygiene::walk_chain(span, frame.body.span.ctxt()) - } + // Match behavior of debuginfo (`FunctionCx::adjusted_span_and_dbg_scope`). + let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body.span); data.push((frame.instance, span.lo())); } diff --git a/tests/pass/backtrace/backtrace-api-v0.stdout b/tests/pass/backtrace/backtrace-api-v0.stdout index c9cab26816..e6644b4453 100644 --- a/tests/pass/backtrace/backtrace-api-v0.stdout +++ b/tests/pass/backtrace/backtrace-api-v0.stdout @@ -1,5 +1,5 @@ $DIR/backtrace-api-v0.rs:24:14 (func_d) -$DIR/backtrace-api-v0.rs:20:5 (func_c) +$DIR/backtrace-api-v0.rs:14:9 (func_c) $DIR/backtrace-api-v0.rs:9:5 (func_b::) $DIR/backtrace-api-v0.rs:5:5 (func_a) $DIR/backtrace-api-v0.rs:29:18 (main) diff --git a/tests/pass/backtrace/backtrace-api-v1.stdout b/tests/pass/backtrace/backtrace-api-v1.stdout index e145c167e8..11efd2f075 100644 --- a/tests/pass/backtrace/backtrace-api-v1.stdout +++ b/tests/pass/backtrace/backtrace-api-v1.stdout @@ -1,5 +1,5 @@ $DIR/backtrace-api-v1.rs:27:9 (func_d) -$DIR/backtrace-api-v1.rs:20:5 (func_c) +$DIR/backtrace-api-v1.rs:14:9 (func_c) $DIR/backtrace-api-v1.rs:9:5 (func_b::) $DIR/backtrace-api-v1.rs:5:5 (func_a) $DIR/backtrace-api-v1.rs:34:18 (main) From 57266e9f016ac8eeda11720b8d779858568c61a3 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 26 Apr 2024 04:58:30 +0000 Subject: [PATCH 102/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index a6433a8e28..a45ecda15c 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -cb3752d20e0f5d24348062211102a08d46fbecff +6acb9e75ebc936df737381a9d0b7a7bccd6f0b2f From 79198aee8e731aaa098aa81938202901ded2c746 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 Apr 2024 17:44:37 +0200 Subject: [PATCH 103/208] add smoke tests for basic PathBuf interactions --- tests/pass/path.rs | 60 ++++++++++++++++++++++++++++++++++++++++ tests/pass/shims/path.rs | 37 ------------------------- 2 files changed, 60 insertions(+), 37 deletions(-) create mode 100644 tests/pass/path.rs delete mode 100644 tests/pass/shims/path.rs diff --git a/tests/pass/path.rs b/tests/pass/path.rs new file mode 100644 index 0000000000..fe99d38e07 --- /dev/null +++ b/tests/pass/path.rs @@ -0,0 +1,60 @@ +//@compile-flags: -Zmiri-disable-isolation +use std::path::{absolute, Path, PathBuf}; + +#[path = "../utils/mod.rs"] +mod utils; + +#[track_caller] +fn assert_absolute_eq(in_: &str, out: &str) { + assert_eq!(absolute(in_).unwrap().as_os_str(), Path::new(out).as_os_str()); +} + +fn test_absolute() { + if cfg!(unix) { + assert_absolute_eq("/a/b/c", "/a/b/c"); + assert_absolute_eq("/a/b/c", "/a/b/c"); + assert_absolute_eq("/a//b/c", "/a/b/c"); + assert_absolute_eq("//a/b/c", "//a/b/c"); + assert_absolute_eq("///a/b/c", "/a/b/c"); + assert_absolute_eq("/a/b/c/", "/a/b/c/"); + assert_absolute_eq("/a/./b/../c/.././..", "/a/b/../c/../.."); + } else if cfg!(windows) { + // Test that all these are unchanged + assert_absolute_eq(r"C:\path\to\file", r"C:\path\to\file"); + assert_absolute_eq(r"C:\path\to\file\", r"C:\path\to\file\"); + assert_absolute_eq(r"\\server\share\to\file", r"\\server\share\to\file"); + assert_absolute_eq(r"\\server.\share.\to\file", r"\\server.\share.\to\file"); + assert_absolute_eq(r"\\.\PIPE\name", r"\\.\PIPE\name"); + assert_absolute_eq(r"\\.\C:\path\to\COM1", r"\\.\C:\path\to\COM1"); + assert_absolute_eq(r"\\?\C:\path\to\file", r"\\?\C:\path\to\file"); + assert_absolute_eq(r"\\?\UNC\server\share\to\file", r"\\?\UNC\server\share\to\file"); + assert_absolute_eq(r"\\?\PIPE\name", r"\\?\PIPE\name"); + // Verbatim paths are always unchanged, no matter what. + assert_absolute_eq(r"\\?\path.\to/file..", r"\\?\path.\to/file.."); + + assert_absolute_eq(r"C:\path..\to.\file.", r"C:\path..\to\file"); + assert_absolute_eq(r"COM1", r"\\.\COM1"); + } else { + panic!("unsupported OS"); + } +} + +fn buf_smoke(mut p: PathBuf) { + for _c in p.components() {} + + p.push("hello"); + for _c in p.components() {} + + if cfg!(windows) { + p.push(r"C:\mydir"); + } else { + p.push(r"/mydir"); + } + for _c in p.components() {} +} + +fn main() { + buf_smoke(PathBuf::new()); + buf_smoke(utils::tmp()); + test_absolute(); +} diff --git a/tests/pass/shims/path.rs b/tests/pass/shims/path.rs deleted file mode 100644 index cadbeb476b..0000000000 --- a/tests/pass/shims/path.rs +++ /dev/null @@ -1,37 +0,0 @@ -//@compile-flags: -Zmiri-disable-isolation -use std::path::{absolute, Path}; - -#[track_caller] -fn test_absolute(in_: &str, out: &str) { - assert_eq!(absolute(in_).unwrap().as_os_str(), Path::new(out).as_os_str()); -} - -fn main() { - if cfg!(unix) { - test_absolute("/a/b/c", "/a/b/c"); - test_absolute("/a/b/c", "/a/b/c"); - test_absolute("/a//b/c", "/a/b/c"); - test_absolute("//a/b/c", "//a/b/c"); - test_absolute("///a/b/c", "/a/b/c"); - test_absolute("/a/b/c/", "/a/b/c/"); - test_absolute("/a/./b/../c/.././..", "/a/b/../c/../.."); - } else if cfg!(windows) { - // Test that all these are unchanged - test_absolute(r"C:\path\to\file", r"C:\path\to\file"); - test_absolute(r"C:\path\to\file\", r"C:\path\to\file\"); - test_absolute(r"\\server\share\to\file", r"\\server\share\to\file"); - test_absolute(r"\\server.\share.\to\file", r"\\server.\share.\to\file"); - test_absolute(r"\\.\PIPE\name", r"\\.\PIPE\name"); - test_absolute(r"\\.\C:\path\to\COM1", r"\\.\C:\path\to\COM1"); - test_absolute(r"\\?\C:\path\to\file", r"\\?\C:\path\to\file"); - test_absolute(r"\\?\UNC\server\share\to\file", r"\\?\UNC\server\share\to\file"); - test_absolute(r"\\?\PIPE\name", r"\\?\PIPE\name"); - // Verbatim paths are always unchanged, no matter what. - test_absolute(r"\\?\path.\to/file..", r"\\?\path.\to/file.."); - - test_absolute(r"C:\path..\to.\file.", r"C:\path..\to\file"); - test_absolute(r"COM1", r"\\.\COM1"); - } else { - panic!("unsupported OS"); - } -} From 0c35628bcc1841526fcf42c02b307381a5d10847 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 Apr 2024 19:44:38 +0200 Subject: [PATCH 104/208] add test for concurrent env var access --- tests/pass/shims/env/var.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/pass/shims/env/var.rs b/tests/pass/shims/env/var.rs index 23a3724ff7..babaf00578 100644 --- a/tests/pass/shims/env/var.rs +++ b/tests/pass/shims/env/var.rs @@ -1,4 +1,5 @@ use std::env; +use std::thread; fn main() { // Test that miri environment is isolated when communication is disabled. @@ -23,4 +24,11 @@ fn main() { env::remove_var("MIRI_TEST"); assert_eq!(env::var("MIRI_TEST"), Err(env::VarError::NotPresent)); println!("{:#?}", env::vars().collect::>()); + + // Do things concurrently, to make sure there's no data race. + let t = thread::spawn(|| { + env::set_var("MIRI_TEST", "42"); + }); + env::set_var("MIRI_TEST", "42"); + t.join().unwrap(); } From c53d44b9b2a96ac9bd656ab5ca3e66721f97f713 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 26 Apr 2024 19:45:18 +0200 Subject: [PATCH 105/208] env: split up Windows and Unix environment variable handling --- src/machine.rs | 11 +- src/shims/env.rs | 549 +++----------------------- src/shims/extern_static.rs | 14 +- src/shims/unix/env.rs | 276 +++++++++++++ src/shims/unix/macos/foreign_items.rs | 11 +- src/shims/unix/mod.rs | 5 +- src/shims/windows/env.rs | 257 ++++++++++++ src/shims/windows/foreign_items.rs | 5 +- src/shims/windows/mod.rs | 8 + 9 files changed, 606 insertions(+), 530 deletions(-) create mode 100644 src/shims/unix/env.rs create mode 100644 src/shims/windows/env.rs diff --git a/src/machine.rs b/src/machine.rs index cbe70cbffe..8b28687177 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -31,7 +31,7 @@ use rustc_target::spec::abi::Abi; use crate::{ concurrency::{data_race, weak_memory}, - shims::unix::FdTable, + shims::unix, *, }; @@ -439,8 +439,7 @@ pub struct MiriMachine<'mir, 'tcx> { /// Ptr-int-cast module global data. pub alloc_addresses: alloc_addresses::GlobalState, - /// Environment variables set by `setenv`. - /// Miri does not expose env vars from the host to the emulated program. + /// Environment variables. pub(crate) env_vars: EnvVars<'tcx>, /// Return place of the main function. @@ -465,9 +464,9 @@ pub struct MiriMachine<'mir, 'tcx> { pub(crate) validate: bool, /// The table of file descriptors. - pub(crate) fds: shims::unix::FdTable, + pub(crate) fds: unix::FdTable, /// The table of directory descriptors. - pub(crate) dirs: shims::unix::DirTable, + pub(crate) dirs: unix::DirTable, /// This machine's monotone clock. pub(crate) clock: Clock, @@ -642,7 +641,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { tls: TlsData::default(), isolated_op: config.isolated_op, validate: config.validate, - fds: FdTable::new(config.mute_stdout_stderr), + fds: unix::FdTable::new(config.mute_stdout_stderr), dirs: Default::default(), layouts, threads: ThreadManager::default(), diff --git a/src/shims/env.rs b/src/shims/env.rs index 298fefdb0f..fc0160fdf2 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -1,33 +1,24 @@ -use std::env; -use std::ffi::{OsStr, OsString}; -use std::io::ErrorKind; -use std::mem; +use std::ffi::OsString; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::Ty; -use rustc_target::abi::Size; use crate::*; -use helpers::windows_check_buffer_size; +use shims::{unix::UnixEnvVars, windows::WindowsEnvVars}; #[derive(Default)] -pub struct EnvVars<'tcx> { - /// Stores pointers to the environment variables. These variables must be stored as - /// null-terminated target strings (c_str or wide_str) with the `"{name}={value}"` format. - map: FxHashMap>>, - - /// Place where the `environ` static is stored. Lazily initialized, but then never changes. - pub(crate) environ: Option>, +pub enum EnvVars<'tcx> { + #[default] + Uninit, + Unix(UnixEnvVars<'tcx>), + Windows(WindowsEnvVars), } impl VisitProvenance for EnvVars<'_> { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { - let EnvVars { map, environ } = self; - - environ.visit_provenance(visit); - for ptr in map.values() { - ptr.visit_provenance(visit); + match self { + EnvVars::Uninit => {} + EnvVars::Unix(env) => env.visit_provenance(visit), + EnvVars::Windows(env) => env.visit_provenance(visit), } } } @@ -39,517 +30,73 @@ impl<'tcx> EnvVars<'tcx> { ) -> InterpResult<'tcx> { // Initialize the `env_vars` map. // Skip the loop entirely if we don't want to forward anything. + let mut env_vars = FxHashMap::default(); if ecx.machine.communicate() || !config.forwarded_env_vars.is_empty() { for (name, value) in &config.env { let forward = ecx.machine.communicate() || config.forwarded_env_vars.iter().any(|v| **v == *name); if forward { - add_env_var(ecx, name, value)?; + env_vars.insert(OsString::from(name), OsString::from(value)); } } } for (name, value) in &config.set_env_vars { - add_env_var(ecx, OsStr::new(name), OsStr::new(value))?; + env_vars.insert(OsString::from(name), OsString::from(value)); } - // Initialize the `environ` pointer when needed. - if ecx.target_os_is_unix() { - // This is memory backing an extern static, hence `ExternStatic`, not `Env`. - let layout = ecx.machine.layouts.mut_raw_ptr; - let place = ecx.allocate(layout, MiriMemoryKind::ExternStatic.into())?; - ecx.write_null(&place)?; - ecx.machine.env_vars.environ = Some(place); - ecx.update_environ()?; - } + let env_vars = if ecx.target_os_is_unix() { + EnvVars::Unix(UnixEnvVars::new(ecx, env_vars)?) + } else if ecx.tcx.sess.target.os == "windows" { + EnvVars::Windows(WindowsEnvVars::new(ecx, env_vars)?) + } else { + // Used e.g. for wasi + EnvVars::Uninit + }; + ecx.machine.env_vars = env_vars; + Ok(()) } pub(crate) fn cleanup<'mir>( ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, ) -> InterpResult<'tcx> { - // Deallocate individual env vars. - let env_vars = mem::take(&mut ecx.machine.env_vars.map); - for (_name, ptr) in env_vars { - ecx.deallocate_ptr(ptr, None, MiriMemoryKind::Runtime.into())?; - } - // Deallocate environ var list. - if ecx.target_os_is_unix() { - let environ = ecx.machine.env_vars.environ.as_ref().unwrap(); - let old_vars_ptr = ecx.read_pointer(environ)?; - ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; - } - Ok(()) - } -} - -fn add_env_var<'mir, 'tcx>( - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - name: &OsStr, - value: &OsStr, -) -> InterpResult<'tcx, ()> { - let var_ptr = match ecx.tcx.sess.target.os.as_ref() { - _ if ecx.target_os_is_unix() => alloc_env_var_as_c_str(name, value, ecx)?, - "windows" => alloc_env_var_as_wide_str(name, value, ecx)?, - unsupported => - throw_unsup_format!( - "environment support for target OS `{}` not yet available", - unsupported - ), - }; - ecx.machine.env_vars.map.insert(name.to_os_string(), var_ptr); - Ok(()) -} - -fn alloc_env_var_as_c_str<'mir, 'tcx>( - name: &OsStr, - value: &OsStr, - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, -) -> InterpResult<'tcx, Pointer>> { - let mut name_osstring = name.to_os_string(); - name_osstring.push("="); - name_osstring.push(value); - ecx.alloc_os_str_as_c_str(name_osstring.as_os_str(), MiriMemoryKind::Runtime.into()) -} - -fn alloc_env_var_as_wide_str<'mir, 'tcx>( - name: &OsStr, - value: &OsStr, - ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, -) -> InterpResult<'tcx, Pointer>> { - let mut name_osstring = name.to_os_string(); - name_osstring.push("="); - name_osstring.push(value); - ecx.alloc_os_str_as_wide_str(name_osstring.as_os_str(), MiriMemoryKind::Runtime.into()) -} - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - fn getenv( - &mut self, - name_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - this.assert_target_os_is_unix("getenv"); - - let name_ptr = this.read_pointer(name_op)?; - let name = this.read_os_str_from_c_str(name_ptr)?; - this.read_environ()?; - Ok(match this.machine.env_vars.map.get(name) { - Some(var_ptr) => { - // The offset is used to strip the "{name}=" part of the string. - var_ptr.offset( - Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), - this, - )? - } - None => Pointer::null(), - }) - } - - #[allow(non_snake_case)] - fn GetEnvironmentVariableW( - &mut self, - name_op: &OpTy<'tcx, Provenance>, // LPCWSTR - buf_op: &OpTy<'tcx, Provenance>, // LPWSTR - size_op: &OpTy<'tcx, Provenance>, // DWORD - ) -> InterpResult<'tcx, Scalar> { - // ^ Returns DWORD (u32 on Windows) - - let this = self.eval_context_mut(); - this.assert_target_os("windows", "GetEnvironmentVariableW"); - - let name_ptr = this.read_pointer(name_op)?; - let buf_ptr = this.read_pointer(buf_op)?; - let buf_size = this.read_scalar(size_op)?.to_u32()?; // in characters - - let name = this.read_os_str_from_wide_str(name_ptr)?; - Ok(match this.machine.env_vars.map.get(&name) { - Some(&var_ptr) => { - // The offset is used to strip the "{name}=" part of the string. - #[rustfmt::skip] - let name_offset_bytes = u64::try_from(name.len()).unwrap() - .checked_add(1).unwrap() - .checked_mul(2).unwrap(); - let var_ptr = var_ptr.offset(Size::from_bytes(name_offset_bytes), this)?; - let var = this.read_os_str_from_wide_str(var_ptr)?; - - Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str( - &var, - buf_ptr, - buf_size.into(), - )?)) - // This can in fact return 0. It is up to the caller to set last_error to 0 - // beforehand and check it afterwards to exclude that case. - } - None => { - let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND"); - this.set_last_error(envvar_not_found)?; - Scalar::from_u32(0) // return zero upon failure - } - }) - } - - #[allow(non_snake_case)] - fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - this.assert_target_os("windows", "GetEnvironmentStringsW"); - - // Info on layout of environment blocks in Windows: - // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables - let mut env_vars = std::ffi::OsString::new(); - for &item in this.machine.env_vars.map.values() { - let env_var = this.read_os_str_from_wide_str(item)?; - env_vars.push(env_var); - env_vars.push("\0"); - } - // Allocate environment block & Store environment variables to environment block. - // Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`. - let envblock_ptr = - this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?; - // If the function succeeds, the return value is a pointer to the environment block of the current process. - Ok(envblock_ptr) - } - - #[allow(non_snake_case)] - fn FreeEnvironmentStringsW( - &mut self, - env_block_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - this.assert_target_os("windows", "FreeEnvironmentStringsW"); - - let env_block_ptr = this.read_pointer(env_block_op)?; - let result = this.deallocate_ptr(env_block_ptr, None, MiriMemoryKind::Runtime.into()); - // If the function succeeds, the return value is nonzero. - Ok(Scalar::from_i32(i32::from(result.is_ok()))) - } - - fn setenv( - &mut self, - name_op: &OpTy<'tcx, Provenance>, - value_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, i32> { - let this = self.eval_context_mut(); - this.assert_target_os_is_unix("setenv"); - - let name_ptr = this.read_pointer(name_op)?; - let value_ptr = this.read_pointer(value_op)?; - - let mut new = None; - if !this.ptr_is_null(name_ptr)? { - let name = this.read_os_str_from_c_str(name_ptr)?; - if !name.is_empty() && !name.to_string_lossy().contains('=') { - let value = this.read_os_str_from_c_str(value_ptr)?; - new = Some((name.to_owned(), value.to_owned())); - } - } - if let Some((name, value)) = new { - let var_ptr = alloc_env_var_as_c_str(&name, &value, this)?; - if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) { - this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; - } - this.update_environ()?; - Ok(0) // return zero on success - } else { - // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - Ok(-1) - } - } - - #[allow(non_snake_case)] - fn SetEnvironmentVariableW( - &mut self, - name_op: &OpTy<'tcx, Provenance>, // LPCWSTR - value_op: &OpTy<'tcx, Provenance>, // LPCWSTR - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - this.assert_target_os("windows", "SetEnvironmentVariableW"); - - let name_ptr = this.read_pointer(name_op)?; - let value_ptr = this.read_pointer(value_op)?; - - if this.ptr_is_null(name_ptr)? { - // ERROR CODE is not clearly explained in docs.. For now, throw UB instead. - throw_ub_format!("pointer to environment variable name is NULL"); - } - - let name = this.read_os_str_from_wide_str(name_ptr)?; - if name.is_empty() { - throw_unsup_format!("environment variable name is an empty string"); - } else if name.to_string_lossy().contains('=') { - throw_unsup_format!("environment variable name contains '='"); - } else if this.ptr_is_null(value_ptr)? { - // Delete environment variable `{name}` - if let Some(var) = this.machine.env_vars.map.remove(&name) { - this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; - } - Ok(this.eval_windows("c", "TRUE")) - } else { - let value = this.read_os_str_from_wide_str(value_ptr)?; - let var_ptr = alloc_env_var_as_wide_str(&name, &value, this)?; - if let Some(var) = this.machine.env_vars.map.insert(name, var_ptr) { - this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; - } - Ok(this.eval_windows("c", "TRUE")) - } - } - - fn unsetenv(&mut self, name_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { - let this = self.eval_context_mut(); - this.assert_target_os_is_unix("unsetenv"); - - let name_ptr = this.read_pointer(name_op)?; - let mut success = None; - if !this.ptr_is_null(name_ptr)? { - let name = this.read_os_str_from_c_str(name_ptr)?.to_owned(); - if !name.is_empty() && !name.to_string_lossy().contains('=') { - success = Some(this.machine.env_vars.map.remove(&name)); - } - } - if let Some(old) = success { - if let Some(var) = old { - this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; - } - this.update_environ()?; - Ok(0) - } else { - // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - Ok(-1) + let this = ecx.eval_context_mut(); + match this.machine.env_vars { + EnvVars::Unix(_) => UnixEnvVars::cleanup(this), + EnvVars::Windows(_) => Ok(()), // no cleanup needed + EnvVars::Uninit => Ok(()), } } - fn getcwd( - &mut self, - buf_op: &OpTy<'tcx, Provenance>, - size_op: &OpTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, Pointer>> { - let this = self.eval_context_mut(); - this.assert_target_os_is_unix("getcwd"); - - let buf = this.read_pointer(buf_op)?; - let size = this.read_target_usize(size_op)?; - - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`getcwd`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(Pointer::null()); + pub(crate) fn unix(&self) -> &UnixEnvVars<'tcx> { + match self { + EnvVars::Unix(env) => env, + _ => unreachable!(), } - - // If we cannot get the current directory, we return null - match env::current_dir() { - Ok(cwd) => { - if this.write_path_to_c_str(&cwd, buf, size)?.0 { - return Ok(buf); - } - let erange = this.eval_libc("ERANGE"); - this.set_last_error(erange)?; - } - Err(e) => this.set_last_error_from_io_error(e.kind())?, - } - - Ok(Pointer::null()) } - #[allow(non_snake_case)] - fn GetCurrentDirectoryW( - &mut self, - size_op: &OpTy<'tcx, Provenance>, // DWORD - buf_op: &OpTy<'tcx, Provenance>, // LPTSTR - ) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); - this.assert_target_os("windows", "GetCurrentDirectoryW"); - - let size = u64::from(this.read_scalar(size_op)?.to_u32()?); - let buf = this.read_pointer(buf_op)?; - - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - return Ok(Scalar::from_u32(0)); + pub(crate) fn unix_mut(&mut self) -> &mut UnixEnvVars<'tcx> { + match self { + EnvVars::Unix(env) => env, + _ => unreachable!(), } - - // If we cannot get the current directory, we return 0 - match env::current_dir() { - Ok(cwd) => { - // This can in fact return 0. It is up to the caller to set last_error to 0 - // beforehand and check it afterwards to exclude that case. - return Ok(Scalar::from_u32(windows_check_buffer_size( - this.write_path_to_wide_str(&cwd, buf, size)?, - ))); - } - Err(e) => this.set_last_error_from_io_error(e.kind())?, - } - Ok(Scalar::from_u32(0)) } - fn chdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { - let this = self.eval_context_mut(); - this.assert_target_os_is_unix("chdir"); - - let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; - - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`chdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - - return Ok(-1); - } - - match env::set_current_dir(path) { - Ok(()) => Ok(0), - Err(e) => { - this.set_last_error_from_io_error(e.kind())?; - Ok(-1) - } + pub(crate) fn windows(&self) -> &WindowsEnvVars { + match self { + EnvVars::Windows(env) => env, + _ => unreachable!(), } } - #[allow(non_snake_case)] - fn SetCurrentDirectoryW( - &mut self, - path_op: &OpTy<'tcx, Provenance>, // LPCTSTR - ) -> InterpResult<'tcx, Scalar> { - // ^ Returns BOOL (i32 on Windows) - - let this = self.eval_context_mut(); - this.assert_target_os("windows", "SetCurrentDirectoryW"); - - let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?; - - if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { - this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; - - return Ok(this.eval_windows("c", "FALSE")); - } - - match env::set_current_dir(path) { - Ok(()) => Ok(this.eval_windows("c", "TRUE")), - Err(e) => { - this.set_last_error_from_io_error(e.kind())?; - Ok(this.eval_windows("c", "FALSE")) - } + pub(crate) fn windows_mut(&mut self) -> &mut WindowsEnvVars { + match self { + EnvVars::Windows(env) => env, + _ => unreachable!(), } } - - /// Updates the `environ` static. - /// The first time it gets called, also initializes `extra.environ`. - fn update_environ(&mut self) -> InterpResult<'tcx> { - let this = self.eval_context_mut(); - // Deallocate the old environ list, if any. - let environ = this.machine.env_vars.environ.as_ref().unwrap().clone(); - let old_vars_ptr = this.read_pointer(&environ)?; - if !this.ptr_is_null(old_vars_ptr)? { - this.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; - } - - // Collect all the pointers to each variable in a vector. - let mut vars: Vec>> = - this.machine.env_vars.map.values().copied().collect(); - // Add the trailing null pointer. - vars.push(Pointer::null()); - // Make an array with all these pointers inside Miri. - let tcx = this.tcx; - let vars_layout = this.layout_of(Ty::new_array( - tcx.tcx, - this.machine.layouts.mut_raw_ptr.ty, - u64::try_from(vars.len()).unwrap(), - ))?; - let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; - for (idx, var) in vars.into_iter().enumerate() { - let place = this.project_field(&vars_place, idx)?; - this.write_pointer(var, &place)?; - } - this.write_pointer(vars_place.ptr(), &environ)?; - - Ok(()) - } - - /// Reads from the `environ` static. - /// We don't actually care about the result, but we care about this potentially causing a data race. - fn read_environ(&self) -> InterpResult<'tcx> { - let this = self.eval_context_ref(); - let environ = this.machine.env_vars.environ.as_ref().unwrap(); - let _vars_ptr = this.read_pointer(environ)?; - Ok(()) - } - - fn getpid(&mut self) -> InterpResult<'tcx, i32> { - let this = self.eval_context_mut(); - this.assert_target_os_is_unix("getpid"); - - this.check_no_isolation("`getpid`")?; - - // The reason we need to do this wacky of a conversion is because - // `libc::getpid` returns an i32, however, `std::process::id()` return an u32. - // So we un-do the conversion that stdlib does and turn it back into an i32. - #[allow(clippy::cast_possible_wrap)] - Ok(std::process::id() as i32) - } - - #[allow(non_snake_case)] - fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> { - let this = self.eval_context_mut(); - this.assert_target_os("windows", "GetCurrentProcessId"); - this.check_no_isolation("`GetCurrentProcessId`")?; - - Ok(std::process::id()) - } - - #[allow(non_snake_case)] - fn GetUserProfileDirectoryW( - &mut self, - token: &OpTy<'tcx, Provenance>, // HANDLE - buf: &OpTy<'tcx, Provenance>, // LPWSTR - size: &OpTy<'tcx, Provenance>, // LPDWORD - ) -> InterpResult<'tcx, Scalar> // returns BOOL - { - let this = self.eval_context_mut(); - this.assert_target_os("windows", "GetUserProfileDirectoryW"); - this.check_no_isolation("`GetUserProfileDirectoryW`")?; - - let token = this.read_target_isize(token)?; - let buf = this.read_pointer(buf)?; - let size = this.deref_pointer(size)?; - - if token != -4 { - throw_unsup_format!( - "GetUserProfileDirectoryW: only CURRENT_PROCESS_TOKEN is supported" - ); - } - - // See for docs. - Ok(match directories::UserDirs::new() { - Some(dirs) => { - let home = dirs.home_dir(); - let size_avail = if this.ptr_is_null(size.ptr())? { - 0 // if the buf pointer is null, we can't write to it; `size` will be updated to the required length - } else { - this.read_scalar(&size)?.to_u32()? - }; - // Of course we cannot use `windows_check_buffer_size` here since this uses - // a different method for dealing with a too-small buffer than the other functions... - let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?; - // The Windows docs just say that this is written on failure. But std - // seems to rely on it always being written. - this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?; - if success { - Scalar::from_i32(1) // return TRUE - } else { - this.set_last_error(this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER"))?; - Scalar::from_i32(0) // return FALSE - } - } - None => { - // We have to pick some error code. - this.set_last_error(this.eval_windows("c", "ERROR_BAD_USER_PROFILE"))?; - Scalar::from_i32(0) // return FALSE - } - }) - } } + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {} diff --git a/src/shims/extern_static.rs b/src/shims/extern_static.rs index 7c4a54fb46..442338a611 100644 --- a/src/shims/extern_static.rs +++ b/src/shims/extern_static.rs @@ -47,20 +47,14 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"], )?; // "environ" - Self::add_extern_static( - this, - "environ", - this.machine.env_vars.environ.as_ref().unwrap().ptr(), - ); + let environ = this.machine.env_vars.unix().environ(); + Self::add_extern_static(this, "environ", environ); } "freebsd" => { Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?; // "environ" - Self::add_extern_static( - this, - "environ", - this.machine.env_vars.environ.as_ref().unwrap().ptr(), - ); + let environ = this.machine.env_vars.unix().environ(); + Self::add_extern_static(this, "environ", environ); } "android" => { Self::null_ptr_extern_statics(this, &["bsd_signal"])?; diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs new file mode 100644 index 0000000000..128f0dcafa --- /dev/null +++ b/src/shims/unix/env.rs @@ -0,0 +1,276 @@ +use std::env; +use std::ffi::{OsStr, OsString}; +use std::io::ErrorKind; +use std::mem; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::Ty; +use rustc_target::abi::Size; + +use crate::*; + +pub struct UnixEnvVars<'tcx> { + /// Stores pointers to the environment variables. These variables must be stored as + /// null-terminated target strings (c_str or wide_str) with the `"{name}={value}"` format. + map: FxHashMap>>, + + /// Place where the `environ` static is stored. Lazily initialized, but then never changes. + environ: MPlaceTy<'tcx, Provenance>, +} + +impl VisitProvenance for UnixEnvVars<'_> { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + let UnixEnvVars { map, environ } = self; + + environ.visit_provenance(visit); + for ptr in map.values() { + ptr.visit_provenance(visit); + } + } +} + +impl<'tcx> UnixEnvVars<'tcx> { + pub(crate) fn new<'mir>( + ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + env_vars: FxHashMap, + ) -> InterpResult<'tcx, Self> { + // Allocate memory for all these env vars. + let mut env_vars_machine = FxHashMap::default(); + for (name, val) in env_vars.into_iter() { + let ptr = alloc_env_var(ecx, &name, &val)?; + env_vars_machine.insert(name, ptr); + } + + // This is memory backing an extern static, hence `ExternStatic`, not `Env`. + let layout = ecx.machine.layouts.mut_raw_ptr; + let environ = ecx.allocate(layout, MiriMemoryKind::ExternStatic.into())?; + let environ_block = alloc_environ_block(ecx, env_vars_machine.values().copied().collect())?; + ecx.write_pointer(environ_block, &environ)?; + + Ok(UnixEnvVars { map: env_vars_machine, environ }) + } + + pub(crate) fn cleanup<'mir>( + ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + ) -> InterpResult<'tcx> { + // Deallocate individual env vars. + let env_vars = mem::take(&mut ecx.machine.env_vars.unix_mut().map); + for (_name, ptr) in env_vars { + ecx.deallocate_ptr(ptr, None, MiriMemoryKind::Runtime.into())?; + } + // Deallocate environ var list. + let environ = &ecx.machine.env_vars.unix().environ; + let old_vars_ptr = ecx.read_pointer(environ)?; + ecx.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; + + Ok(()) + } + + pub(crate) fn environ(&self) -> Pointer> { + self.environ.ptr() + } +} + +fn alloc_env_var<'mir, 'tcx>( + ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + name: &OsStr, + value: &OsStr, +) -> InterpResult<'tcx, Pointer>> { + let mut name_osstring = name.to_os_string(); + name_osstring.push("="); + name_osstring.push(value); + ecx.alloc_os_str_as_c_str(name_osstring.as_os_str(), MiriMemoryKind::Runtime.into()) +} + +/// Allocates an `environ` block with the given list of pointers. +fn alloc_environ_block<'mir, 'tcx>( + ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + mut vars: Vec>>, +) -> InterpResult<'tcx, Pointer>> { + // Add trailing null. + vars.push(Pointer::null()); + // Make an array with all these pointers inside Miri. + let vars_layout = ecx.layout_of(Ty::new_array( + *ecx.tcx, + ecx.machine.layouts.mut_raw_ptr.ty, + u64::try_from(vars.len()).unwrap(), + ))?; + let vars_place = ecx.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; + for (idx, var) in vars.into_iter().enumerate() { + let place = ecx.project_field(&vars_place, idx)?; + ecx.write_pointer(var, &place)?; + } + Ok(vars_place.ptr()) +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + fn getenv( + &mut self, + name_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("getenv"); + + let name_ptr = this.read_pointer(name_op)?; + let name = this.read_os_str_from_c_str(name_ptr)?; + + // We don't care about the value as we have the `map` to keep track of everything, + // but we do want to do this read so it shows up as a data race. + let _vars_ptr = this.read_pointer(&this.machine.env_vars.unix().environ)?; + Ok(match this.machine.env_vars.unix().map.get(name) { + Some(var_ptr) => { + // The offset is used to strip the "{name}=" part of the string. + var_ptr.offset( + Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), + this, + )? + } + None => Pointer::null(), + }) + } + + fn setenv( + &mut self, + name_op: &OpTy<'tcx, Provenance>, + value_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("setenv"); + + let name_ptr = this.read_pointer(name_op)?; + let value_ptr = this.read_pointer(value_op)?; + + let mut new = None; + if !this.ptr_is_null(name_ptr)? { + let name = this.read_os_str_from_c_str(name_ptr)?; + if !name.is_empty() && !name.to_string_lossy().contains('=') { + let value = this.read_os_str_from_c_str(value_ptr)?; + new = Some((name.to_owned(), value.to_owned())); + } + } + if let Some((name, value)) = new { + let var_ptr = alloc_env_var(this, &name, &value)?; + if let Some(var) = this.machine.env_vars.unix_mut().map.insert(name, var_ptr) { + this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; + } + this.update_environ()?; + Ok(0) // return zero on success + } else { + // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. + let einval = this.eval_libc("EINVAL"); + this.set_last_error(einval)?; + Ok(-1) + } + } + + fn unsetenv(&mut self, name_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("unsetenv"); + + let name_ptr = this.read_pointer(name_op)?; + let mut success = None; + if !this.ptr_is_null(name_ptr)? { + let name = this.read_os_str_from_c_str(name_ptr)?.to_owned(); + if !name.is_empty() && !name.to_string_lossy().contains('=') { + success = Some(this.machine.env_vars.unix_mut().map.remove(&name)); + } + } + if let Some(old) = success { + if let Some(var) = old { + this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?; + } + this.update_environ()?; + Ok(0) + } else { + // name argument is a null pointer, points to an empty string, or points to a string containing an '=' character. + let einval = this.eval_libc("EINVAL"); + this.set_last_error(einval)?; + Ok(-1) + } + } + + fn getcwd( + &mut self, + buf_op: &OpTy<'tcx, Provenance>, + size_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("getcwd"); + + let buf = this.read_pointer(buf_op)?; + let size = this.read_target_usize(size_op)?; + + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`getcwd`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(Pointer::null()); + } + + // If we cannot get the current directory, we return null + match env::current_dir() { + Ok(cwd) => { + if this.write_path_to_c_str(&cwd, buf, size)?.0 { + return Ok(buf); + } + let erange = this.eval_libc("ERANGE"); + this.set_last_error(erange)?; + } + Err(e) => this.set_last_error_from_io_error(e.kind())?, + } + + Ok(Pointer::null()) + } + + fn chdir(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("chdir"); + + let path = this.read_path_from_c_str(this.read_pointer(path_op)?)?; + + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`chdir`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + + return Ok(-1); + } + + match env::set_current_dir(path) { + Ok(()) => Ok(0), + Err(e) => { + this.set_last_error_from_io_error(e.kind())?; + Ok(-1) + } + } + } + + /// Updates the `environ` static. + fn update_environ(&mut self) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + // Deallocate the old environ list. + let environ = this.machine.env_vars.unix().environ.clone(); + let old_vars_ptr = this.read_pointer(&environ)?; + this.deallocate_ptr(old_vars_ptr, None, MiriMemoryKind::Runtime.into())?; + + // Write the new list. + let vals = this.machine.env_vars.unix().map.values().copied().collect(); + let environ_block = alloc_environ_block(this, vals)?; + this.write_pointer(environ_block, &environ)?; + + Ok(()) + } + + fn getpid(&mut self) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + this.assert_target_os_is_unix("getpid"); + + this.check_no_isolation("`getpid`")?; + + // The reason we need to do this wacky of a conversion is because + // `libc::getpid` returns an i32, however, `std::process::id()` return an u32. + // So we un-do the conversion that stdlib does and turn it back into an i32. + #[allow(clippy::cast_possible_wrap)] + Ok(std::process::id() as i32) + } +} diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 53a02bf5e0..66a8dce753 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -74,15 +74,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Environment related shims "_NSGetEnviron" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_pointer( - this.machine - .env_vars - .environ - .as_ref() - .expect("machine must be initialized") - .ptr(), - dest, - )?; + let environ = this.machine.env_vars.unix().environ(); + this.write_pointer(environ, dest)?; } // Time related shims diff --git a/src/shims/unix/mod.rs b/src/shims/unix/mod.rs index 2bc41e1a62..144593aa2f 100644 --- a/src/shims/unix/mod.rs +++ b/src/shims/unix/mod.rs @@ -1,5 +1,6 @@ pub mod foreign_items; +mod env; mod fd; mod fs; mod mem; @@ -11,9 +12,11 @@ mod freebsd; mod linux; mod macos; +pub use env::UnixEnvVars; pub use fd::{FdTable, FileDescriptor}; pub use fs::DirTable; -// All the unix-specific extension traits +// All the Unix-specific extension traits +pub use env::EvalContextExt as _; pub use fd::EvalContextExt as _; pub use fs::EvalContextExt as _; pub use mem::EvalContextExt as _; diff --git a/src/shims/windows/env.rs b/src/shims/windows/env.rs new file mode 100644 index 0000000000..e91623ac87 --- /dev/null +++ b/src/shims/windows/env.rs @@ -0,0 +1,257 @@ +use std::env; +use std::ffi::OsString; +use std::io::ErrorKind; + +use rustc_data_structures::fx::FxHashMap; + +use crate::*; +use helpers::windows_check_buffer_size; + +#[derive(Default)] +pub struct WindowsEnvVars { + /// Stores the environment varialbles. + map: FxHashMap, +} + +impl VisitProvenance for WindowsEnvVars { + fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { + let WindowsEnvVars { map: _ } = self; + } +} + +impl WindowsEnvVars { + pub(crate) fn new<'mir, 'tcx>( + _ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + env_vars: FxHashMap, + ) -> InterpResult<'tcx, Self> { + Ok(Self { map: env_vars }) + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + #[allow(non_snake_case)] + fn GetEnvironmentVariableW( + &mut self, + name_op: &OpTy<'tcx, Provenance>, // LPCWSTR + buf_op: &OpTy<'tcx, Provenance>, // LPWSTR + size_op: &OpTy<'tcx, Provenance>, // DWORD + ) -> InterpResult<'tcx, Scalar> { + // ^ Returns DWORD (u32 on Windows) + + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetEnvironmentVariableW"); + + let name_ptr = this.read_pointer(name_op)?; + let buf_ptr = this.read_pointer(buf_op)?; + let buf_size = this.read_scalar(size_op)?.to_u32()?; // in characters + + let name = this.read_os_str_from_wide_str(name_ptr)?; + Ok(match this.machine.env_vars.windows().map.get(&name).cloned() { + Some(val) => { + Scalar::from_u32(windows_check_buffer_size(this.write_os_str_to_wide_str( + &val, + buf_ptr, + buf_size.into(), + )?)) + // This can in fact return 0. It is up to the caller to set last_error to 0 + // beforehand and check it afterwards to exclude that case. + } + None => { + let envvar_not_found = this.eval_windows("c", "ERROR_ENVVAR_NOT_FOUND"); + this.set_last_error(envvar_not_found)?; + Scalar::from_u32(0) // return zero upon failure + } + }) + } + + #[allow(non_snake_case)] + fn GetEnvironmentStringsW(&mut self) -> InterpResult<'tcx, Pointer>> { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetEnvironmentStringsW"); + + // Info on layout of environment blocks in Windows: + // https://docs.microsoft.com/en-us/windows/win32/procthread/environment-variables + let mut env_vars = std::ffi::OsString::new(); + for (name, value) in this.machine.env_vars.windows().map.iter() { + env_vars.push(name); + env_vars.push("="); + env_vars.push(value); + env_vars.push("\0"); + } + // Allocate environment block & Store environment variables to environment block. + // Final null terminator(block terminator) is added by `alloc_os_str_to_wide_str`. + let envblock_ptr = + this.alloc_os_str_as_wide_str(&env_vars, MiriMemoryKind::Runtime.into())?; + // If the function succeeds, the return value is a pointer to the environment block of the current process. + Ok(envblock_ptr) + } + + #[allow(non_snake_case)] + fn FreeEnvironmentStringsW( + &mut self, + env_block_op: &OpTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "FreeEnvironmentStringsW"); + + let env_block_ptr = this.read_pointer(env_block_op)?; + this.deallocate_ptr(env_block_ptr, None, MiriMemoryKind::Runtime.into())?; + // If the function succeeds, the return value is nonzero. + Ok(Scalar::from_i32(1)) + } + + #[allow(non_snake_case)] + fn SetEnvironmentVariableW( + &mut self, + name_op: &OpTy<'tcx, Provenance>, // LPCWSTR + value_op: &OpTy<'tcx, Provenance>, // LPCWSTR + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "SetEnvironmentVariableW"); + + let name_ptr = this.read_pointer(name_op)?; + let value_ptr = this.read_pointer(value_op)?; + + if this.ptr_is_null(name_ptr)? { + // ERROR CODE is not clearly explained in docs.. For now, throw UB instead. + throw_ub_format!("pointer to environment variable name is NULL"); + } + + let name = this.read_os_str_from_wide_str(name_ptr)?; + if name.is_empty() { + throw_unsup_format!("environment variable name is an empty string"); + } else if name.to_string_lossy().contains('=') { + throw_unsup_format!("environment variable name contains '='"); + } else if this.ptr_is_null(value_ptr)? { + // Delete environment variable `{name}` if it exists. + this.machine.env_vars.windows_mut().map.remove(&name); + Ok(this.eval_windows("c", "TRUE")) + } else { + let value = this.read_os_str_from_wide_str(value_ptr)?; + this.machine.env_vars.windows_mut().map.insert(name, value); + Ok(this.eval_windows("c", "TRUE")) + } + } + + #[allow(non_snake_case)] + fn GetCurrentDirectoryW( + &mut self, + size_op: &OpTy<'tcx, Provenance>, // DWORD + buf_op: &OpTy<'tcx, Provenance>, // LPTSTR + ) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetCurrentDirectoryW"); + + let size = u64::from(this.read_scalar(size_op)?.to_u32()?); + let buf = this.read_pointer(buf_op)?; + + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + return Ok(Scalar::from_u32(0)); + } + + // If we cannot get the current directory, we return 0 + match env::current_dir() { + Ok(cwd) => { + // This can in fact return 0. It is up to the caller to set last_error to 0 + // beforehand and check it afterwards to exclude that case. + return Ok(Scalar::from_u32(windows_check_buffer_size( + this.write_path_to_wide_str(&cwd, buf, size)?, + ))); + } + Err(e) => this.set_last_error_from_io_error(e.kind())?, + } + Ok(Scalar::from_u32(0)) + } + + #[allow(non_snake_case)] + fn SetCurrentDirectoryW( + &mut self, + path_op: &OpTy<'tcx, Provenance>, // LPCTSTR + ) -> InterpResult<'tcx, Scalar> { + // ^ Returns BOOL (i32 on Windows) + + let this = self.eval_context_mut(); + this.assert_target_os("windows", "SetCurrentDirectoryW"); + + let path = this.read_path_from_wide_str(this.read_pointer(path_op)?)?; + + if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { + this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + + return Ok(this.eval_windows("c", "FALSE")); + } + + match env::set_current_dir(path) { + Ok(()) => Ok(this.eval_windows("c", "TRUE")), + Err(e) => { + this.set_last_error_from_io_error(e.kind())?; + Ok(this.eval_windows("c", "FALSE")) + } + } + } + + #[allow(non_snake_case)] + fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetCurrentProcessId"); + this.check_no_isolation("`GetCurrentProcessId`")?; + + Ok(std::process::id()) + } + + #[allow(non_snake_case)] + fn GetUserProfileDirectoryW( + &mut self, + token: &OpTy<'tcx, Provenance>, // HANDLE + buf: &OpTy<'tcx, Provenance>, // LPWSTR + size: &OpTy<'tcx, Provenance>, // LPDWORD + ) -> InterpResult<'tcx, Scalar> // returns BOOL + { + let this = self.eval_context_mut(); + this.assert_target_os("windows", "GetUserProfileDirectoryW"); + this.check_no_isolation("`GetUserProfileDirectoryW`")?; + + let token = this.read_target_isize(token)?; + let buf = this.read_pointer(buf)?; + let size = this.deref_pointer(size)?; + + if token != -4 { + throw_unsup_format!( + "GetUserProfileDirectoryW: only CURRENT_PROCESS_TOKEN is supported" + ); + } + + // See for docs. + Ok(match directories::UserDirs::new() { + Some(dirs) => { + let home = dirs.home_dir(); + let size_avail = if this.ptr_is_null(size.ptr())? { + 0 // if the buf pointer is null, we can't write to it; `size` will be updated to the required length + } else { + this.read_scalar(&size)?.to_u32()? + }; + // Of course we cannot use `windows_check_buffer_size` here since this uses + // a different method for dealing with a too-small buffer than the other functions... + let (success, len) = this.write_path_to_wide_str(home, buf, size_avail.into())?; + // The Windows docs just say that this is written on failure. But std + // seems to rely on it always being written. + this.write_scalar(Scalar::from_u32(len.try_into().unwrap()), &size)?; + if success { + Scalar::from_i32(1) // return TRUE + } else { + this.set_last_error(this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER"))?; + Scalar::from_i32(0) // return FALSE + } + } + None => { + // We have to pick some error code. + this.set_last_error(this.eval_windows("c", "ERROR_BAD_USER_PROFILE"))?; + Scalar::from_i32(0) // return FALSE + } + }) + } +} diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 24f7cd18e7..e8ae80261c 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -10,11 +10,10 @@ use rustc_target::spec::abi::Abi; use crate::shims::alloc::EvalContextExt as _; use crate::shims::os_str::bytes_to_os_str; +use crate::shims::windows::*; use crate::*; use shims::foreign_items::EmulateForeignItemResult; -use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; -use shims::windows::sync::EvalContextExt as _; -use shims::windows::thread::EvalContextExt as _; +use shims::windows::handle::{Handle, PseudoHandle}; fn is_dyn_sym(name: &str) -> bool { // std does dynamic detection for these symbols diff --git a/src/shims/windows/mod.rs b/src/shims/windows/mod.rs index 7688abe412..65f682b9da 100644 --- a/src/shims/windows/mod.rs +++ b/src/shims/windows/mod.rs @@ -1,5 +1,13 @@ pub mod foreign_items; +mod env; mod handle; mod sync; mod thread; + +pub use env::WindowsEnvVars; +// All the Windows-specific extension traits +pub use env::EvalContextExt as _; +pub use handle::EvalContextExt as _; +pub use sync::EvalContextExt as _; +pub use thread::EvalContextExt as _; From 0132d4ce0d227c1bf13b58301be9d9cab5d8df8e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 27 Apr 2024 08:57:55 +0200 Subject: [PATCH 106/208] josh rustc-pull: check that no new root commits get created --- miri-script/src/commands.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 66707dee5e..575bf4a15d 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -257,12 +257,26 @@ impl Command { }) .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; + // This should not add any new root commits. So count those before and after merging. + let num_roots = || -> Result { + Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") + .read() + .context("failed to determine the number of root commits")? + .parse::()?) + }; + let num_roots_before = num_roots()?; + // Merge the fetched commit. const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") .run() .context("FAILED to merge new commits, something went wrong")?; + // Check that the number of roots did not increase. + if num_roots()? != num_roots_before { + bail!("Josh created a new root commit. This is probably not the history you want."); + } + drop(josh); Ok(()) } From 565f9a92114d431774c48bd2476c52c4ed722f9a Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Sat, 27 Apr 2024 00:15:39 -0700 Subject: [PATCH 107/208] fix: usage of `deprecated` version of `Node.js` --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0af334a654..3f714fc932 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,7 @@ jobs: # over time). - name: Add cache for cargo id: cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | # Taken from . @@ -95,7 +95,7 @@ jobs: # over time). - name: Add cache for cargo id: cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | # Taken from . From 1f3afd87cf21e694b9258dababe1e9d0a164981e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sat, 27 Apr 2024 16:23:44 +0200 Subject: [PATCH 108/208] Add doc comment to `pack_generic` --- src/shims/x86/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index cf4d6a04be..e04519d9c0 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -1127,6 +1127,13 @@ fn pmulhrsw<'tcx>( Ok(()) } +/// Packs two N-bit integer vectors to a single N/2-bit integers. +/// +/// The conversion from N-bit to N/2-bit should be provided by `f`. +/// +/// Each 128-bit chunk is treated independently (i.e., the value for +/// the is i-th 128-bit chunk of `dest` is calculated with the i-th +/// 128-bit chunks of `left` and `right`). fn pack_generic<'tcx>( this: &mut crate::MiriInterpCx<'_, 'tcx>, left: &OpTy<'tcx, Provenance>, From a0987d62b9539cc283ec427b52c66a08ad7e1b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sat, 27 Apr 2024 16:41:27 +0200 Subject: [PATCH 109/208] Clarify behavior of AVX2 gather when dest and offsets have different numbers of elements --- src/shims/x86/avx2.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shims/x86/avx2.rs b/src/shims/x86/avx2.rs index bbf53f9f1e..ba361ec655 100644 --- a/src/shims/x86/avx2.rs +++ b/src/shims/x86/avx2.rs @@ -71,6 +71,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let (dest, dest_len) = this.mplace_to_simd(dest)?; // There are cases like dest: i32x4, offsets: i64x2 + // If dest has more elements than offset, extra dest elements are filled with zero. + // If offsets has more elements than dest, extra offsets are ignored. let actual_len = dest_len.min(offsets_len); assert_eq!(dest_len, mask_len); From fc11813367cdd71fa98e5c3697be66f1d8273826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sat, 27 Apr 2024 17:43:39 +0200 Subject: [PATCH 110/208] Do not implement x86 SIMD abs with host integers --- src/shims/x86/mod.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index e04519d9c0..9a36286a75 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -739,14 +739,20 @@ fn int_abs<'tcx>( assert_eq!(op_len, dest_len); + let zero = ImmTy::from_int(0, op.layout.field(this, 0)); + for i in 0..dest_len { - let op = this.read_scalar(&this.project_index(&op, i)?)?; + let op = this.read_immediate(&this.project_index(&op, i)?)?; let dest = this.project_index(&dest, i)?; - // Converting to a host "i128" works since the input is always signed. - let res = op.to_int(dest.layout.size)?.unsigned_abs(); + let lt_zero = this.wrapping_binary_op(mir::BinOp::Lt, &op, &zero)?; + let res = if lt_zero.to_scalar().to_bool()? { + this.wrapping_unary_op(mir::UnOp::Neg, &op)? + } else { + op + }; - this.write_scalar(Scalar::from_uint(res, dest.layout.size), &dest)?; + this.write_immediate(*res, &dest)?; } Ok(()) From c0387184486e98b6d9c2903fb104fe43ebc0c79b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 28 Apr 2024 09:38:54 +0200 Subject: [PATCH 111/208] file descriptors: make write take &mut self --- src/shims/unix/fd.rs | 16 ++++++++-------- src/shims/unix/fs.rs | 4 ++-- src/shims/unix/linux/eventfd.rs | 14 +++++--------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/shims/unix/fd.rs b/src/shims/unix/fd.rs index a5fe38b902..18a41f6c66 100644 --- a/src/shims/unix/fd.rs +++ b/src/shims/unix/fd.rs @@ -25,7 +25,7 @@ pub trait FileDescriptor: std::fmt::Debug + Any { } fn write<'tcx>( - &self, + &mut self, _communicate_allowed: bool, _bytes: &[u8], _tcx: TyCtxt<'tcx>, @@ -103,13 +103,13 @@ impl FileDescriptor for io::Stdout { } fn write<'tcx>( - &self, + &mut self, _communicate_allowed: bool, bytes: &[u8], _tcx: TyCtxt<'tcx>, ) -> InterpResult<'tcx, io::Result> { // We allow writing to stderr even with isolation enabled. - let result = Write::write(&mut { self }, bytes); + let result = Write::write(self, bytes); // Stdout is buffered, flush to make sure it appears on the // screen. This is the write() syscall of the interpreted // program, we want it to correspond to a write() syscall on @@ -135,7 +135,7 @@ impl FileDescriptor for io::Stderr { } fn write<'tcx>( - &self, + &mut self, _communicate_allowed: bool, bytes: &[u8], _tcx: TyCtxt<'tcx>, @@ -164,7 +164,7 @@ impl FileDescriptor for NullOutput { } fn write<'tcx>( - &self, + &mut self, _communicate_allowed: bool, bytes: &[u8], _tcx: TyCtxt<'tcx>, @@ -418,10 +418,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .min(u64::try_from(isize::MAX).unwrap()); let communicate = this.machine.communicate(); - if let Some(file_descriptor) = this.machine.fds.get(fd) { - let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?; + let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned(); + if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { let result = file_descriptor - .write(communicate, bytes, *this.tcx)? + .write(communicate, &bytes, *this.tcx)? .map(|c| i64::try_from(c).unwrap()); this.try_unwrap_io_result(result) } else { diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index ebf9f43c19..0bf0e3d52c 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -39,13 +39,13 @@ impl FileDescriptor for FileHandle { } fn write<'tcx>( - &self, + &mut self, communicate_allowed: bool, bytes: &[u8], _tcx: TyCtxt<'tcx>, ) -> InterpResult<'tcx, io::Result> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); - Ok((&mut &self.file).write(bytes)) + Ok(self.file.write(bytes)) } fn seek<'tcx>( diff --git a/src/shims/unix/linux/eventfd.rs b/src/shims/unix/linux/eventfd.rs index 0f28b69ac4..452527017f 100644 --- a/src/shims/unix/linux/eventfd.rs +++ b/src/shims/unix/linux/eventfd.rs @@ -1,6 +1,5 @@ //! Linux `eventfd` implementation. //! Currently just a stub. -use std::cell::Cell; use std::io; use rustc_middle::ty::TyCtxt; @@ -20,7 +19,7 @@ use crate::*; struct Event { /// The object contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the /// kernel. This counter is initialized with the value specified in the argument initval. - val: Cell, + val: u64, } impl FileDescriptor for Event { @@ -30,7 +29,7 @@ impl FileDescriptor for Event { fn dup(&mut self) -> io::Result> { // FIXME: this is wrong, the new and old FD should refer to the same event object! - Ok(Box::new(Event { val: self.val.clone() })) + Ok(Box::new(Event { val: self.val })) } fn close<'tcx>( @@ -53,12 +52,11 @@ impl FileDescriptor for Event { /// supplied buffer is less than 8 bytes, or if an attempt is /// made to write the value 0xffffffffffffffff. fn write<'tcx>( - &self, + &mut self, _communicate_allowed: bool, bytes: &[u8], tcx: TyCtxt<'tcx>, ) -> InterpResult<'tcx, io::Result> { - let v1 = self.val.get(); let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size // Convert from target endianness to host endianness. let num = match tcx.sess.target.endian { @@ -67,9 +65,7 @@ impl FileDescriptor for Event { }; // FIXME handle blocking when addition results in exceeding the max u64 value // or fail with EAGAIN if the file descriptor is nonblocking. - let v2 = v1.checked_add(num).unwrap(); - self.val.set(v2); - assert_eq!(8, bytes.len()); + self.val = self.val.checked_add(num).unwrap(); Ok(Ok(8)) } } @@ -119,7 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("eventfd: EFD_SEMAPHORE is unsupported"); } - let fd = this.machine.fds.insert_fd(Box::new(Event { val: Cell::new(val.into()) })); + let fd = this.machine.fds.insert_fd(Box::new(Event { val: val.into() })); Ok(Scalar::from_i32(fd)) } } From 66c7f6d43bb077a9ddbe0cc2ed513df7be88abb0 Mon Sep 17 00:00:00 2001 From: George Bateman Date: Sun, 28 Apr 2024 14:13:08 +0100 Subject: [PATCH 112/208] Remove direct dependencies on lazy_static, once_cell and byteorder The functionality of all three crates is now available in the standard library. --- Cargo.toml | 1 - tests/ui.rs | 22 +++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b00dae784d..ac8b4b37e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,6 @@ colored = "2" ui_test = "0.21.1" rustc_version = "0.4" regex = "1.5.5" -lazy_static = "1.4.0" tempfile = "3" [package.metadata.rust-analyzer] diff --git a/tests/ui.rs b/tests/ui.rs index ace0da0125..efeefbe29f 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -1,6 +1,7 @@ use std::ffi::OsString; use std::num::NonZeroUsize; use std::path::{Path, PathBuf}; +use std::sync::OnceLock; use std::{env, process::Command}; use colored::*; @@ -67,8 +68,8 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> let mut config = Config { target: Some(target.to_owned()), - stderr_filters: STDERR.clone(), - stdout_filters: STDOUT.clone(), + stderr_filters: stderr_filters().into(), + stdout_filters: stdout_filters().into(), mode, program, out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("ui"), @@ -174,15 +175,18 @@ fn run_tests( } macro_rules! regexes { - ($name:ident: $($regex:expr => $replacement:expr,)*) => {lazy_static::lazy_static! { - static ref $name: Vec<(Match, &'static [u8])> = vec![ - $((Regex::new($regex).unwrap().into(), $replacement.as_bytes()),)* - ]; - }}; + ($name:ident: $($regex:expr => $replacement:expr,)*) => { + fn $name() -> &'static [(Match, &'static [u8])] { + static S: OnceLock> = OnceLock::new(); + S.get_or_init(|| vec![ + $((Regex::new($regex).unwrap().into(), $replacement.as_bytes()),)* + ]) + } + }; } regexes! { - STDOUT: + stdout_filters: // Windows file paths r"\\" => "/", // erase borrow tags @@ -191,7 +195,7 @@ regexes! { } regexes! { - STDERR: + stderr_filters: // erase line and column info r"\.rs:[0-9]+:[0-9]+(: [0-9]+:[0-9]+)?" => ".rs:LL:CC", // erase alloc ids From 69ed2365519a15afee0b376f20fd09283a8824ab Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 27 Apr 2024 14:55:39 -0400 Subject: [PATCH 113/208] Use the interpreted program's TZ variable in localtime_r --- Cargo.lock | 201 +++++++++++++++------------------------ Cargo.toml | 3 +- src/shims/env.rs | 15 ++- src/shims/time.rs | 26 +++-- src/shims/unix/env.rs | 21 ++++ src/shims/windows/env.rs | 9 +- 6 files changed, 137 insertions(+), 138 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 293b937a5e..3a282e8931 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,21 +37,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "annotate-snippets" version = "0.9.2" @@ -121,12 +106,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "camino" version = "1.1.6" @@ -177,10 +156,29 @@ version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ - "android-tzdata", - "iana-time-zone", "num-traits", - "windows-targets 0.52.3", +] + +[[package]] +name = "chrono-tz" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", ] [[package]] @@ -249,12 +247,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" version = "0.2.12" @@ -379,29 +371,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "indenter" version = "0.3.3" @@ -455,15 +424,6 @@ dependencies = [ "libc", ] -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -587,6 +547,7 @@ version = "0.1.0" dependencies = [ "aes", "chrono", + "chrono-tz", "colored", "ctrlc", "directories", @@ -690,6 +651,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + [[package]] name = "perf-event-open-sys" version = "3.0.0" @@ -699,6 +669,44 @@ dependencies = [ "libc", ] +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -931,6 +939,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "smallvec" version = "1.13.1" @@ -1094,60 +1108,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - [[package]] name = "winapi" version = "0.3.9" @@ -1170,15 +1130,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.3", -] - [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index b00dae784d..de7a6de9e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,8 @@ smallvec = "1.7" aes = { version = "0.8.3", features = ["hazmat"] } measureme = "11" ctrlc = "3.2.5" -chrono = { version = "0.4.38", default-features = false, features = ["clock"] } +chrono = { version = "0.4.38", default-features = false } +chrono-tz = "0.9" directories = "5" # Copied from `compiler/rustc/Cargo.toml`. diff --git a/src/shims/env.rs b/src/shims/env.rs index fc0160fdf2..395a1ca62c 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -1,4 +1,4 @@ -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use rustc_data_structures::fx::FxHashMap; @@ -99,4 +99,15 @@ impl<'tcx> EnvVars<'tcx> { } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Try to get an environment variable from the interpreted program's environment. This is + /// useful for implementing shims which are documented to read from the environment. + fn get_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { + let this = self.eval_context_ref(); + match &this.machine.env_vars { + EnvVars::Uninit => return Ok(None), + EnvVars::Unix(vars) => vars.get(this, name), + EnvVars::Windows(vars) => vars.get(name), + } + } +} diff --git a/src/shims/time.rs b/src/shims/time.rs index dfdf58470d..05dbdef1ba 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -1,8 +1,10 @@ -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::fmt::Write; +use std::str::FromStr; use std::time::{Duration, SystemTime}; -use chrono::{DateTime, Datelike, Local, Timelike, Utc}; +use chrono::{DateTime, Datelike, Offset, Timelike, Utc}; +use chrono_tz::Tz; use crate::concurrency::thread::MachineCallback; use crate::*; @@ -136,8 +138,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .unwrap(); let dt_utc: DateTime = DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp"); + + // Figure out what time zone is in use + let tz = this.get_var(OsStr::new("TZ"))?.unwrap_or_else(|| OsString::from("UTC")); + let tz = match tz.into_string() { + Ok(tz) => Tz::from_str(&tz).unwrap_or(Tz::UTC), + _ => Tz::UTC, + }; + // Convert that to local time, then return the broken-down time value. - let dt: DateTime = DateTime::from(dt_utc); + let dt: DateTime = dt_utc.with_timezone(&tz); // This value is always set to -1, because there is no way to know if dst is in effect with // chrono crate yet. @@ -146,17 +156,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08. // This may not be consistent with libc::localtime_r's result. - let offset_in_second = Local::now().offset().local_minus_utc(); - let tm_gmtoff = offset_in_second; + let offset_in_seconds = dt.offset().fix().local_minus_utc(); + let tm_gmtoff = offset_in_seconds; let mut tm_zone = String::new(); - if offset_in_second < 0 { + if offset_in_seconds < 0 { tm_zone.push('-'); } else { tm_zone.push('+'); } - let offset_hour = offset_in_second.abs() / 3600; + let offset_hour = offset_in_seconds.abs() / 3600; write!(tm_zone, "{:02}", offset_hour).unwrap(); - let offset_min = (offset_in_second.abs() % 3600) / 60; + let offset_min = (offset_in_seconds.abs() % 3600) / 60; if offset_min != 0 { write!(tm_zone, "{:02}", offset_min).unwrap(); } diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs index 128f0dcafa..f61a81b07c 100644 --- a/src/shims/unix/env.rs +++ b/src/shims/unix/env.rs @@ -70,6 +70,27 @@ impl<'tcx> UnixEnvVars<'tcx> { pub(crate) fn environ(&self) -> Pointer> { self.environ.ptr() } + + /// Implementation detail for [`InterpCx::get_var`]. This basically does `getenv`, complete + /// with the reads of the environment, but returns an [`OsString`] instead of a pointer. + pub(crate) fn get<'mir>( + &self, + ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + name: &OsStr, + ) -> InterpResult<'tcx, Option> { + // We don't care about the value as we have the `map` to keep track of everything, + // but we do want to do this read so it shows up as a data race. + let _vars_ptr = ecx.read_pointer(&self.environ)?; + let Some(var_ptr) = self.map.get(name) else { + return Ok(None); + }; + // The offset is used to strip the "{name}=" part of the string. + let var_ptr = var_ptr.offset( + Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), + ecx, + )?; + ecx.read_os_str_from_c_str(var_ptr).map(|s| Some(s.to_owned())) + } } fn alloc_env_var<'mir, 'tcx>( diff --git a/src/shims/windows/env.rs b/src/shims/windows/env.rs index e91623ac87..a8b0a77b23 100644 --- a/src/shims/windows/env.rs +++ b/src/shims/windows/env.rs @@ -1,5 +1,5 @@ use std::env; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::io::ErrorKind; use rustc_data_structures::fx::FxHashMap; @@ -9,7 +9,7 @@ use helpers::windows_check_buffer_size; #[derive(Default)] pub struct WindowsEnvVars { - /// Stores the environment varialbles. + /// Stores the environment variables. map: FxHashMap, } @@ -26,6 +26,11 @@ impl WindowsEnvVars { ) -> InterpResult<'tcx, Self> { Ok(Self { map: env_vars }) } + + /// Implementation detail for [`InterpCx::get_var`]. + pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option> { + Ok(self.map.get(name).cloned()) + } } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} From ec878af906d265d3063424cda196dd576c38ae8c Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 28 Apr 2024 17:45:08 -0400 Subject: [PATCH 114/208] Refactor UnixEnvVars::get so that it can be reused by getenv --- src/shims/env.rs | 12 ++++++++++-- src/shims/time.rs | 2 +- src/shims/unix/env.rs | 21 +++++---------------- src/shims/windows/env.rs | 2 +- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/shims/env.rs b/src/shims/env.rs index 395a1ca62c..b95abb484c 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -102,11 +102,19 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Try to get an environment variable from the interpreted program's environment. This is /// useful for implementing shims which are documented to read from the environment. - fn get_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { + fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); match &this.machine.env_vars { EnvVars::Uninit => return Ok(None), - EnvVars::Unix(vars) => vars.get(this, name), + EnvVars::Unix(vars) => { + let var_ptr = vars.get(this, name)?; + if let Some(ptr) = var_ptr { + let var = this.read_os_str_from_c_str(ptr)?; + Ok(Some(var.to_owned())) + } else { + Ok(None) + } + } EnvVars::Windows(vars) => vars.get(name), } } diff --git a/src/shims/time.rs b/src/shims/time.rs index 05dbdef1ba..8d1f07f916 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -140,7 +140,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp"); // Figure out what time zone is in use - let tz = this.get_var(OsStr::new("TZ"))?.unwrap_or_else(|| OsString::from("UTC")); + let tz = this.get_env_var(OsStr::new("TZ"))?.unwrap_or_else(|| OsString::from("UTC")); let tz = match tz.into_string() { Ok(tz) => Tz::from_str(&tz).unwrap_or(Tz::UTC), _ => Tz::UTC, diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs index f61a81b07c..910e53260b 100644 --- a/src/shims/unix/env.rs +++ b/src/shims/unix/env.rs @@ -71,13 +71,13 @@ impl<'tcx> UnixEnvVars<'tcx> { self.environ.ptr() } - /// Implementation detail for [`InterpCx::get_var`]. This basically does `getenv`, complete + /// Implementation detail for [`InterpCx::get_env_var`]. This basically does `getenv`, complete /// with the reads of the environment, but returns an [`OsString`] instead of a pointer. pub(crate) fn get<'mir>( &self, ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, name: &OsStr, - ) -> InterpResult<'tcx, Option> { + ) -> InterpResult<'tcx, Option>>> { // We don't care about the value as we have the `map` to keep track of everything, // but we do want to do this read so it shows up as a data race. let _vars_ptr = ecx.read_pointer(&self.environ)?; @@ -89,7 +89,7 @@ impl<'tcx> UnixEnvVars<'tcx> { Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), ecx, )?; - ecx.read_os_str_from_c_str(var_ptr).map(|s| Some(s.to_owned())) + Ok(Some(var_ptr)) } } @@ -137,19 +137,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name_ptr = this.read_pointer(name_op)?; let name = this.read_os_str_from_c_str(name_ptr)?; - // We don't care about the value as we have the `map` to keep track of everything, - // but we do want to do this read so it shows up as a data race. - let _vars_ptr = this.read_pointer(&this.machine.env_vars.unix().environ)?; - Ok(match this.machine.env_vars.unix().map.get(name) { - Some(var_ptr) => { - // The offset is used to strip the "{name}=" part of the string. - var_ptr.offset( - Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), - this, - )? - } - None => Pointer::null(), - }) + let var_ptr = this.machine.env_vars.unix().get(this, name)?; + Ok(var_ptr.unwrap_or_else(Pointer::null)) } fn setenv( diff --git a/src/shims/windows/env.rs b/src/shims/windows/env.rs index a8b0a77b23..b3bc5d4d85 100644 --- a/src/shims/windows/env.rs +++ b/src/shims/windows/env.rs @@ -27,7 +27,7 @@ impl WindowsEnvVars { Ok(Self { map: env_vars }) } - /// Implementation detail for [`InterpCx::get_var`]. + /// Implementation detail for [`InterpCx::get_env_var`]. pub(crate) fn get<'tcx>(&self, name: &OsStr) -> InterpResult<'tcx, Option> { Ok(self.map.get(name).cloned()) } From 92715f5624122d2c0041b10f73aacae7710f442b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 29 Apr 2024 17:33:35 +0200 Subject: [PATCH 115/208] don't leak UnixEnvVars impl details into get_env_var --- src/shims/env.rs | 10 +--------- src/shims/unix/env.rs | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/shims/env.rs b/src/shims/env.rs index b95abb484c..695d113875 100644 --- a/src/shims/env.rs +++ b/src/shims/env.rs @@ -106,15 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_ref(); match &this.machine.env_vars { EnvVars::Uninit => return Ok(None), - EnvVars::Unix(vars) => { - let var_ptr = vars.get(this, name)?; - if let Some(ptr) = var_ptr { - let var = this.read_os_str_from_c_str(ptr)?; - Ok(Some(var.to_owned())) - } else { - Ok(None) - } - } + EnvVars::Unix(vars) => vars.get(this, name), EnvVars::Windows(vars) => vars.get(name), } } diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs index 910e53260b..9082d13da8 100644 --- a/src/shims/unix/env.rs +++ b/src/shims/unix/env.rs @@ -71,9 +71,7 @@ impl<'tcx> UnixEnvVars<'tcx> { self.environ.ptr() } - /// Implementation detail for [`InterpCx::get_env_var`]. This basically does `getenv`, complete - /// with the reads of the environment, but returns an [`OsString`] instead of a pointer. - pub(crate) fn get<'mir>( + fn get_ptr<'mir>( &self, ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, name: &OsStr, @@ -91,6 +89,22 @@ impl<'tcx> UnixEnvVars<'tcx> { )?; Ok(Some(var_ptr)) } + + /// Implementation detail for [`InterpCx::get_env_var`]. This basically does `getenv`, complete + /// with the reads of the environment, but returns an [`OsString`] instead of a pointer. + pub(crate) fn get<'mir>( + &self, + ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, + name: &OsStr, + ) -> InterpResult<'tcx, Option> { + let var_ptr = self.get_ptr(ecx, name)?; + if let Some(ptr) = var_ptr { + let var = ecx.read_os_str_from_c_str(ptr)?; + Ok(Some(var.to_owned())) + } else { + Ok(None) + } + } } fn alloc_env_var<'mir, 'tcx>( @@ -137,7 +151,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let name_ptr = this.read_pointer(name_op)?; let name = this.read_os_str_from_c_str(name_ptr)?; - let var_ptr = this.machine.env_vars.unix().get(this, name)?; + let var_ptr = this.machine.env_vars.unix().get_ptr(this, name)?; Ok(var_ptr.unwrap_or_else(Pointer::null)) } From 38afd5d12f43c10ec1ce60b343e672c6673afd50 Mon Sep 17 00:00:00 2001 From: Paul Gey Date: Wed, 1 May 2024 21:27:49 +0200 Subject: [PATCH 116/208] =?UTF-8?q?Don=E2=80=99t=20print=20`Preparing=20a?= =?UTF-8?q?=20sysroot`=20when=20`-q`/`--quiet`=20is=20passed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cargo-miri/src/phases.rs | 3 ++- cargo-miri/src/setup.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index b774ca8fa7..6a3b6f68a7 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -87,6 +87,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { ), }; let verbose = num_arg_flag("-v"); + let quiet = has_arg_flag("-q") || has_arg_flag("--quiet"); // Determine the involved architectures. let rustc_version = VersionMeta::for_command(miri_for_host()).unwrap_or_else(|err| { @@ -110,7 +111,7 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { } // We always setup. - let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose); + let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose, quiet); // Invoke actual cargo for the job, but with different flags. // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but diff --git a/cargo-miri/src/setup.rs b/cargo-miri/src/setup.rs index 401e9158fa..9a58e6fa01 100644 --- a/cargo-miri/src/setup.rs +++ b/cargo-miri/src/setup.rs @@ -19,6 +19,7 @@ pub fn setup( target: &str, rustc_version: &VersionMeta, verbose: usize, + quiet: bool, ) -> PathBuf { let only_setup = matches!(subcommand, MiriCommand::Setup); let ask_user = !only_setup; @@ -119,6 +120,9 @@ pub fn setup( for _ in 0..verbose { command.arg("-v"); } + if quiet { + command.arg("--quiet"); + } } else { // Suppress output. command.stdout(process::Stdio::null()); @@ -134,7 +138,7 @@ pub fn setup( let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"]; // Do the build. - if print_sysroot { + if print_sysroot || quiet { // Be silent. } else { let mut msg = String::new(); @@ -169,7 +173,7 @@ pub fn setup( ) } }); - if print_sysroot { + if print_sysroot || quiet { // Be silent. } else if only_setup { eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display()); From 63f4291f14244baa57cb507583d4fc4739dcf2cc Mon Sep 17 00:00:00 2001 From: Paul Gey Date: Wed, 1 May 2024 21:34:50 +0200 Subject: [PATCH 117/208] fix usage example for `--print-sysroot` --- cargo-miri/src/phases.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cargo-miri/src/phases.rs b/cargo-miri/src/phases.rs index 6a3b6f68a7..e2fc2a0c27 100644 --- a/cargo-miri/src/phases.rs +++ b/cargo-miri/src/phases.rs @@ -28,7 +28,7 @@ Examples: cargo miri run cargo miri test -- test-suite-filter - cargo miri setup --print sysroot + cargo miri setup --print-sysroot This will print the path to the generated sysroot (and nothing else) on stdout. stderr will still contain progress information about how the build is doing. From 503e82ab144c7d81e83b12b981697c24f05d7280 Mon Sep 17 00:00:00 2001 From: Paul Gey Date: Wed, 1 May 2024 21:50:45 +0200 Subject: [PATCH 118/208] no longer strip `Preparing a sysroot` message from test output --- test-cargo-miri/run-test.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test-cargo-miri/run-test.py b/test-cargo-miri/run-test.py index cac11dff77..2639d29b73 100755 --- a/test-cargo-miri/run-test.py +++ b/test-cargo-miri/run-test.py @@ -34,10 +34,6 @@ def normalize_stdout(str): str = re.sub("finished in \\d+\\.\\d\\ds", "finished in $TIME", str) # the time keeps changing, obviously return str -def normalize_stderr(str): - str = re.sub("Preparing a sysroot for Miri \\(target: [a-z0-9_-]+\\)\\.\\.\\. done\n", "", str) # remove leading cargo-miri setup output - return str - def check_output(actual, path, name): if os.environ.get("RUSTC_BLESS", "0") != "0": # Write the output only if bless is set @@ -69,7 +65,7 @@ def test(name, cmd, stdout_ref, stderr_ref, stdin=b'', env=None): ) (stdout, stderr) = p.communicate(input=stdin) stdout = normalize_stdout(stdout.decode("UTF-8")) - stderr = normalize_stderr(stderr.decode("UTF-8")) + stderr = stderr.decode("UTF-8") stdout_matches = check_output(stdout, stdout_ref, "stdout") stderr_matches = check_output(stderr, stderr_ref, "stderr") From dc1709a49b9da9b221602ec31ceb846ae5227565 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 May 2024 11:09:01 +0200 Subject: [PATCH 119/208] Tree Borrows: first apply transition, then check protector with new 'initialized' --- src/borrow_tracker/tree_borrows/tree.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borrow_tracker/tree_borrows/tree.rs b/src/borrow_tracker/tree_borrows/tree.rs index 2470624181..ff4589657a 100644 --- a/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/borrow_tracker/tree_borrows/tree.rs @@ -106,6 +106,8 @@ impl LocationState { let old_perm = self.permission; let transition = Permission::perform_access(access_kind, rel_pos, old_perm, protected) .ok_or(TransitionError::ChildAccessForbidden(old_perm))?; + self.initialized |= !rel_pos.is_foreign(); + self.permission = transition.applied(old_perm).unwrap(); // Why do only initialized locations cause protector errors? // Consider two mutable references `x`, `y` into disjoint parts of // the same allocation. A priori, these may actually both be used to @@ -123,8 +125,6 @@ impl LocationState { if protected && self.initialized && transition.produces_disabled() { return Err(TransitionError::ProtectedDisabled(old_perm)); } - self.permission = transition.applied(old_perm).unwrap(); - self.initialized |= !rel_pos.is_foreign(); Ok(transition) } From 579d616cfc5cad5009e09bad2e159548534e114f Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 2 May 2024 17:50:51 +0200 Subject: [PATCH 120/208] Don't use `Ty::new_unit` in miri --- src/machine.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine.rs b/src/machine.rs index 8b28687177..4dacb6db8d 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -380,7 +380,7 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit); let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); Ok(Self { - unit: layout_cx.layout_of(Ty::new_unit(tcx))?, + unit: layout_cx.layout_of(tcx.types.unit)?, i8: layout_cx.layout_of(tcx.types.i8)?, i16: layout_cx.layout_of(tcx.types.i16)?, i32: layout_cx.layout_of(tcx.types.i32)?, From 74cf21e88f26d8b3d692be03d7d3a9ccf6e4de01 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 May 2024 18:47:36 +0200 Subject: [PATCH 121/208] interpret: hide some reexports in rustdoc --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 44727e01ea..f47c84842b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,8 +89,10 @@ mod shims; // Establish a "crate-wide prelude": we often import `crate::*`. // Make all those symbols available in the same place as our own. +#[doc(no_inline)] pub use rustc_const_eval::interpret::*; // Resolve ambiguity. +#[doc(no_inline)] pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _}; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; From 79a157a85a94d0526817bd7e41d3bec3f023334e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 2 May 2024 19:13:28 +0200 Subject: [PATCH 122/208] update host-float comments --- src/shims/foreign_items.rs | 12 ++++++------ src/shims/intrinsics/mod.rs | 16 ++++++++-------- src/shims/intrinsics/simd.rs | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 636361148a..4b96ff18b7 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -730,7 +730,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { => { let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let f = this.read_scalar(f)?.to_f32()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); let res = match link_name.as_str() { "cbrtf" => f_host.cbrt(), @@ -761,7 +761,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let f2 = this.read_scalar(f2)?.to_f32()?; // underscore case for windows, here and below // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let res = match link_name.as_str() { "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), @@ -787,7 +787,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { => { let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let f = this.read_scalar(f)?.to_f64()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); let res = match link_name.as_str() { "cbrt" => f_host.cbrt(), @@ -818,7 +818,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let f2 = this.read_scalar(f2)?.to_f64()?; // underscore case for windows, here and below // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let res = match link_name.as_str() { "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), @@ -848,7 +848,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let x = this.read_scalar(x)?.to_f32()?; let signp = this.deref_pointer(signp)?; - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; let res = this.adjust_nan(res.to_soft(), &[x]); @@ -859,7 +859,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let x = this.read_scalar(x)?.to_f64()?; let signp = this.deref_pointer(signp)?; - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let (res, sign) = x.to_host().ln_gamma(); this.write_int(sign, &signp)?; let res = this.adjust_nan(res.to_soft(), &[x]); diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs index d16d5d99e9..a7ba4fd7f9 100644 --- a/src/shims/intrinsics/mod.rs +++ b/src/shims/intrinsics/mod.rs @@ -193,12 +193,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); let res = match intrinsic_name { "sinf32" => f_host.sin(), "cosf32" => f_host.cos(), - "sqrtf32" => f_host.sqrt(), + "sqrtf32" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats "expf32" => f_host.exp(), "exp2f32" => f_host.exp2(), "logf32" => f_host.ln(), @@ -238,12 +238,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); let res = match intrinsic_name { "sinf64" => f_host.sin(), "cosf64" => f_host.cos(), - "sqrtf64" => f_host.sqrt(), + "sqrtf64" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats "expf64" => f_host.exp(), "exp2f64" => f_host.exp2(), "logf64" => f_host.ln(), @@ -366,7 +366,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [f1, f2] = check_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f1.to_host().powf(f2.to_host()).to_soft(); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; @@ -376,7 +376,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [f1, f2] = check_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f1.to_host().powf(f2.to_host()).to_soft(); let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; @@ -386,7 +386,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [f, i] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; let i = this.read_scalar(i)?.to_i32()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; @@ -396,7 +396,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let [f, i] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; let i = this.read_scalar(i)?.to_i32()?; - // FIXME: Using host floats. + // Using host floats (but it's fine, this operation does not have guaranteed precision). let res = f.to_host().powi(i).to_soft(); let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index 9a0671430d..e6d6f72404 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -99,14 +99,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ty::Float(float_ty) = op.layout.ty.kind() else { span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) }; - // FIXME using host floats + // Using host floats (but it's fine, these operations do not have guaranteed precision). match float_ty { FloatTy::F16 => unimplemented!("f16_f128"), FloatTy::F32 => { let f = op.to_scalar().to_f32()?; let f_host = f.to_host(); let res = match host_op { - "fsqrt" => f_host.sqrt(), + "fsqrt" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats "fsin" => f_host.sin(), "fcos" => f_host.cos(), "fexp" => f_host.exp(), From 2d0cc5cc8156534e5ccac34abc2a9ef64c46cf5a Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sun, 28 Apr 2024 18:02:21 +0200 Subject: [PATCH 123/208] Change `SIGPIPE` ui from `#[unix_sigpipe = "..."]` to `-Zon-broken-pipe=...` In the stabilization attempt of `#[unix_sigpipe = "sig_dfl"]`, a concern was raised related to using a language attribute for the feature: Long term, we want `fn lang_start()` to be definable by any crate, not just libstd. Having a special language attribute in that case becomes awkward. So as a first step towards towards the next stabilization attempt, this PR changes the `#[unix_sigpipe = "..."]` attribute to a compiler flag `-Zon-broken-pipe=...` to remove that concern, since now the language is not "contaminated" by this feature. Another point was also raised, namely that the ui should not leak **how** it does things, but rather what the **end effect** is. The new flag uses the proposed naming. This is of course something that can be iterated on further before stabilization. --- src/eval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval.rs b/src/eval.rs index 2242768a56..97c5e9a0ea 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -387,7 +387,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( let main_ptr = ecx.fn_ptr(FnVal::Instance(entry_instance)); // Always using DEFAULT is okay since we don't support signals in Miri anyway. - // (This means we are effectively ignoring `#[unix_sigpipe]`.) + // (This means we are effectively ignoring `-Zon-broken-pipe`.) let sigpipe = rustc_session::config::sigpipe::DEFAULT; ecx.call_function( From 1abbbe577c85b8500d3471fb35b1357694e35217 Mon Sep 17 00:00:00 2001 From: tiif Date: Fri, 3 May 2024 07:05:29 +0800 Subject: [PATCH 124/208] Add assign --- triagebot.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 3b767b3e62..22dc0c49aa 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -10,5 +10,14 @@ allow-unauthenticated = [ # Gives us the commands 'ready', 'author', 'blocked' [shortcut] +# Gives us 'claim', 'release-assignment', 'assign @user' +[assign] +# If set, posts a warning message if the PR is opened against a non-default +# branch (usually main or master). +warn_non_default_branch = true +# If set, the welcome message to new contributors will include this link to +# a contributing guide. +contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html" + [no-merges] exclude_titles = ["Rustup"] From 041eb8f21eab24fa0e277f21a10a1de04cb6b727 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 3 May 2024 04:56:14 +0000 Subject: [PATCH 125/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index a45ecda15c..dcd7b0698a 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -6acb9e75ebc936df737381a9d0b7a7bccd6f0b2f +79734f1db8dbe322192dea32c0f6b80ab14c4c1d From b83a61b38fe6027895f81aeecdbd60d1992b6f94 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 May 2024 08:00:24 +0200 Subject: [PATCH 126/208] update comments and URL --- triagebot.toml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 22dc0c49aa..addb36418d 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -10,14 +10,10 @@ allow-unauthenticated = [ # Gives us the commands 'ready', 'author', 'blocked' [shortcut] -# Gives us 'claim', 'release-assignment', 'assign @user' +# Enables assigning users to issues and PRs. [assign] -# If set, posts a warning message if the PR is opened against a non-default -# branch (usually main or master). warn_non_default_branch = true -# If set, the welcome message to new contributors will include this link to -# a contributing guide. -contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html" +contributing_url = "https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md" [no-merges] exclude_titles = ["Rustup"] From 9de0f4bf1fcf8fb0c087946739fb65aad546791b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 May 2024 08:10:16 +0200 Subject: [PATCH 127/208] update lockfile --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3a282e8931..6960c1661c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -553,7 +553,6 @@ dependencies = [ "directories", "getrandom", "jemalloc-sys", - "lazy_static", "libc", "libffi", "libloading", From 7b7e96d8495870673edcc95ae453f91a845e1247 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 23 Apr 2024 13:12:17 +0000 Subject: [PATCH 128/208] Let miri and const eval execute intrinsics' fallback bodies --- src/machine.rs | 2 +- src/shims/intrinsics/atomic.rs | 6 ++--- src/shims/intrinsics/mod.rs | 45 ++++++++++++++-------------------- src/shims/intrinsics/simd.rs | 6 ++--- 4 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 8b28687177..6a4a1822ce 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -986,7 +986,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { dest: &MPlaceTy<'tcx, Provenance>, ret: Option, unwind: mir::UnwindAction, - ) -> InterpResult<'tcx> { + ) -> InterpResult<'tcx, Option>> { ecx.call_intrinsic(instance, args, dest, ret, unwind) } diff --git a/src/shims/intrinsics/atomic.rs b/src/shims/intrinsics/atomic.rs index 865886a7fc..f566ed3470 100644 --- a/src/shims/intrinsics/atomic.rs +++ b/src/shims/intrinsics/atomic.rs @@ -19,7 +19,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { intrinsic_name: &str, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect(); @@ -113,9 +113,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; } - _ => throw_unsup_format!("unimplemented intrinsic: `atomic_{intrinsic_name}`"), + _ => return Ok(false), } - Ok(()) + Ok(true) } } diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs index d16d5d99e9..cc70b22a16 100644 --- a/src/shims/intrinsics/mod.rs +++ b/src/shims/intrinsics/mod.rs @@ -26,12 +26,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest: &MPlaceTy<'tcx, Provenance>, ret: Option, _unwind: mir::UnwindAction, - ) -> InterpResult<'tcx> { + ) -> InterpResult<'tcx, Option>> { let this = self.eval_context_mut(); // See if the core engine can handle this intrinsic. if this.emulate_intrinsic(instance, args, dest, ret)? { - return Ok(()); + return Ok(None); } let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); @@ -54,16 +54,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Some intrinsics are special and need the "ret". match intrinsic_name { - "catch_unwind" => return this.handle_catch_unwind(args, dest, ret), + "catch_unwind" => { + this.handle_catch_unwind(args, dest, ret)?; + return Ok(None); + } _ => {} } // The rest jumps to `ret` immediately. - this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest)?; + if !this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest)? { + if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { + throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") + } + return Ok(Some(ty::Instance { + def: ty::InstanceDef::Item(instance.def_id()), + args: instance.args, + })) + } trace!("{:?}", this.dump_place(&dest.clone().into())); this.go_to_block(ret); - Ok(()) + Ok(None) } /// Emulates a Miri-supported intrinsic (not supported by the core engine). @@ -73,7 +84,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); if let Some(name) = intrinsic_name.strip_prefix("atomic_") { @@ -84,24 +95,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } match intrinsic_name { - // Miri overwriting CTFE intrinsics. - "ptr_guaranteed_cmp" => { - let [left, right] = check_arg_count(args)?; - let left = this.read_immediate(left)?; - let right = this.read_immediate(right)?; - let val = this.wrapping_binary_op(mir::BinOp::Eq, &left, &right)?; - // We're type punning a bool as an u8 here. - this.write_scalar(val.to_scalar(), dest)?; - } - "const_allocate" => { - // For now, for compatibility with the run-time implementation of this, we just return null. - // See . - this.write_null(dest)?; - } - "const_deallocate" => { - // complete NOP - } - // Raw memory accesses "volatile_load" => { let [place] = check_arg_count(args)?; @@ -425,9 +418,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } - name => throw_unsup_format!("unimplemented intrinsic: `{name}`"), + _ => return Ok(false), } - Ok(()) + Ok(true) } } diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index 9a0671430d..6e768dfdea 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -22,7 +22,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); match intrinsic_name { #[rustfmt::skip] @@ -743,9 +743,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"), + _ => return Ok(false), } - Ok(()) + Ok(true) } fn fminmax_op( From e646e70788ed915d3e5787c89f964bb81afa572c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 23 Apr 2024 13:32:30 +0000 Subject: [PATCH 129/208] Ensure miri only uses fallback bodies that have manually been vetted to preserve all UB that the native intrinsic would have --- src/shims/intrinsics/atomic.rs | 1 + src/shims/intrinsics/mod.rs | 8 ++++++++ src/shims/intrinsics/simd.rs | 1 + tests/fail/intrinsic_fallback_checks_ub.rs | 14 ++++++++++++++ tests/fail/intrinsic_fallback_checks_ub.stderr | 14 ++++++++++++++ 5 files changed, 38 insertions(+) create mode 100644 tests/fail/intrinsic_fallback_checks_ub.rs create mode 100644 tests/fail/intrinsic_fallback_checks_ub.stderr diff --git a/src/shims/intrinsics/atomic.rs b/src/shims/intrinsics/atomic.rs index f566ed3470..4c5e2192e5 100644 --- a/src/shims/intrinsics/atomic.rs +++ b/src/shims/intrinsics/atomic.rs @@ -14,6 +14,7 @@ pub enum AtomicOp { impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Calls the atomic intrinsic `intrinsic`; the `atomic_` prefix has already been removed. + /// Returns `Ok(true)` if the intrinsic was handled. fn emulate_atomic_intrinsic( &mut self, intrinsic_name: &str, diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs index cc70b22a16..2bf6980200 100644 --- a/src/shims/intrinsics/mod.rs +++ b/src/shims/intrinsics/mod.rs @@ -11,6 +11,7 @@ use rustc_middle::{ ty::{self, FloatTy}, }; use rustc_target::abi::Size; +use rustc_span::{sym, Symbol}; use crate::*; use atomic::EvalContextExt as _; @@ -48,6 +49,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // All remaining supported intrinsics have a return place. let ret = match ret { + // FIXME: add fallback body support once we actually have a diverging intrinsic with a fallback body None => throw_unsup_format!("unimplemented (diverging) intrinsic: `{intrinsic_name}`"), Some(p) => p, }; @@ -63,9 +65,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // The rest jumps to `ret` immediately. if !this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest)? { + // We haven't handled the intrinsic, let's see if we can use a fallback body. if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") } + let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub"); + if this.tcx.get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_checks_ub]).next().is_none() { + throw_unsup_format!("miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that"); + } return Ok(Some(ty::Instance { def: ty::InstanceDef::Item(instance.def_id()), args: instance.args, @@ -78,6 +85,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Emulates a Miri-supported intrinsic (not supported by the core engine). + /// Returns `Ok(true)` if the intrinsic was handled. fn emulate_intrinsic_by_name( &mut self, intrinsic_name: &str, diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index 6e768dfdea..dca4c4ea5a 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -16,6 +16,7 @@ pub(crate) enum MinMax { impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed. + /// Returns `Ok(true)` if the intrinsic was handled. fn emulate_simd_intrinsic( &mut self, intrinsic_name: &str, diff --git a/tests/fail/intrinsic_fallback_checks_ub.rs b/tests/fail/intrinsic_fallback_checks_ub.rs new file mode 100644 index 0000000000..93c9d3d781 --- /dev/null +++ b/tests/fail/intrinsic_fallback_checks_ub.rs @@ -0,0 +1,14 @@ +#![feature(rustc_attrs, effects)] + +#[rustc_intrinsic] +#[rustc_nounwind] +#[rustc_do_not_const_check] +#[inline] +pub const fn ptr_guaranteed_cmp(ptr: *const T, other: *const T) -> u8 { + (ptr == other) as u8 +} + +fn main() { + ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); + //~^ ERROR: can only use intrinsic fallback bodies that check UB. +} diff --git a/tests/fail/intrinsic_fallback_checks_ub.stderr b/tests/fail/intrinsic_fallback_checks_ub.stderr new file mode 100644 index 0000000000..b37e05c62f --- /dev/null +++ b/tests/fail/intrinsic_fallback_checks_ub.stderr @@ -0,0 +1,14 @@ +error: unsupported operation: miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that + --> $DIR/intrinsic_fallback_checks_ub.rs:LL:CC + | +LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = note: BACKTRACE: + = note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From fba22f07e390fa11fc0b8c99d55a814279f75d1c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 May 2024 19:36:33 +0200 Subject: [PATCH 130/208] run clippy on a Windows host --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f714fc932..3d7ec210da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,6 +78,12 @@ jobs: rustc -Vv cargo -V + # The `style` job only runs on Linux; this makes sure the Windows-host-specific + # code is also covered by clippy. + - name: Check clippy + if: ${{ matrix.os == 'windows-latest' }} + run: ./miri clippy -- -D warnings + - name: Test Miri run: ./ci/ci.sh From b107850a79c31b13f402b6a20632ecf89b16d33a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 May 2024 19:52:08 +0200 Subject: [PATCH 131/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index dcd7b0698a..b2e6353778 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -79734f1db8dbe322192dea32c0f6b80ab14c4c1d +d6d3b342e85272f5e75c0d7a1dd3a1d8becb40ac From cac4a8e7c4fcb68095627c166d5b79d8d8f65682 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 May 2024 20:31:27 +0200 Subject: [PATCH 132/208] ./miri run: support -v flag to print what it is doing --- miri-script/src/commands.rs | 21 ++++++++++++--------- miri-script/src/main.rs | 32 +++++++++++++++++--------------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 575bf4a15d..b460c7eba5 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -162,7 +162,7 @@ impl Command { Command::Build { flags } => Self::build(flags), Command::Check { flags } => Self::check(flags), Command::Test { bless, flags } => Self::test(bless, flags), - Command::Run { dep, flags } => Self::run(dep, flags), + Command::Run { dep, verbose, flags } => Self::run(dep, verbose, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { flags } => Self::clippy(flags), Command::Cargo { flags } => Self::cargo(flags), @@ -495,7 +495,7 @@ impl Command { Ok(()) } - fn run(dep: bool, mut flags: Vec) -> Result<()> { + fn run(dep: bool, verbose: bool, mut flags: Vec) -> Result<()> { let mut e = MiriEnv::new()?; // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so // that we set the MIRI_SYSROOT up the right way. We must make sure that @@ -522,7 +522,7 @@ impl Command { } // Prepare a sysroot, and add it to the flags. - let miri_sysroot = e.build_miri_sysroot(/* quiet */ true)?; + let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose)?; flags.push("--sysroot".into()); flags.push(miri_sysroot.into()); @@ -532,17 +532,20 @@ impl Command { let miri_flags = flagsplit(&miri_flags); let toolchain = &e.toolchain; let extra_flags = &e.cargo_extra_flags; - if dep { + let quiet_flag = if verbose { None } else { Some("--quiet") }; + let mut cmd = if dep { cmd!( e.sh, - "cargo +{toolchain} --quiet test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {flags...}" - ).quiet().run()?; + "cargo +{toolchain} {quiet_flag...} test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {flags...}" + ) } else { cmd!( e.sh, - "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}" - ).quiet().run()?; - } + "cargo +{toolchain} {quiet_flag...} run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}" + ) + }; + cmd.set_quiet(!verbose); + cmd.run()?; Ok(()) } diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index 712180be28..4904744cb9 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -38,6 +38,7 @@ pub enum Command { /// (Also respects MIRIFLAGS environment variable.) Run { dep: bool, + verbose: bool, /// Flags that are passed through to `miri`. flags: Vec, }, @@ -90,7 +91,7 @@ Just check miri. are passed to `cargo check`. Build miri, set up a sysroot and then run the test suite. are passed to the final `cargo test` invocation. -./miri run [--dep] : +./miri run [--dep] [-v|--verbose] : Build miri, set up a sysroot and then run the driver with the given . (Also respects MIRIFLAGS environment variable.) @@ -132,10 +133,10 @@ Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest rustc commit. The fetched commit is stored in the `rust-version` file, so the next `./miri toolchain` will install the rustc that just got pulled. -./miri rustc-push : +./miri rustc-push []: Push Miri changes back to the rustc repo. This will pull a copy of the rustc history into the Miri repo, unless you set the RUSTC_GIT env var to an existing -clone of the rustc repo. +clone of the rustc repo. The branch defaults to `miri-sync`. ENVIRONMENT VARIABLES @@ -162,12 +163,18 @@ fn main() -> Result<()> { Command::Test { bless, flags: args.collect() } } Some("run") => { - let dep = args.peek().is_some_and(|a| a.to_str() == Some("--dep")); - if dep { - // Consume the flag. + let mut dep = false; + let mut verbose = false; + while let Some(arg) = args.peek().and_then(|a| a.to_str()) { + match arg { + "--dep" => dep = true, + "-v" | "--verbose" => verbose = true, + _ => break, // not for us + } + // Consume the flag, look at the next one. args.next().unwrap(); } - Command::Run { dep, flags: args.collect() } + Command::Run { dep, verbose, flags: args.collect() } } Some("fmt") => Command::Fmt { flags: args.collect() }, Some("clippy") => Command::Clippy { flags: args.collect() }, @@ -187,17 +194,12 @@ fn main() -> Result<()> { let github_user = args .next() .ok_or_else(|| { - anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER BRANCH`") - })? - .to_string_lossy() - .into_owned(); - let branch = args - .next() - .ok_or_else(|| { - anyhow!("Missing second argument for `./miri rustc-push GITHUB_USER BRANCH`") + anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`") })? .to_string_lossy() .into_owned(); + let branch = + args.next().unwrap_or_else(|| "miri-sync".into()).to_string_lossy().into_owned(); if args.next().is_some() { bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`"); } From 8a0a51d1816ec65f1e2af911799b6ba06278c4f0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 3 May 2024 21:45:03 +0200 Subject: [PATCH 133/208] CI: no need to surround if: condition in expansion braces --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d7ec210da..3bc4163ab5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,7 @@ jobs: restore-keys: cargo-${{ runner.os }}-reset20240425 - name: Install tools - if: ${{ steps.cache.outputs.cache-hit != 'true' }} + if: steps.cache.outputs.cache-hit != 'true' run: cargo install -f rustup-toolchain-install-master hyperfine - name: Install miri toolchain @@ -81,7 +81,7 @@ jobs: # The `style` job only runs on Linux; this makes sure the Windows-host-specific # code is also covered by clippy. - name: Check clippy - if: ${{ matrix.os == 'windows-latest' }} + if: matrix.os == 'windows-latest' run: ./miri clippy -- -D warnings - name: Test Miri @@ -117,7 +117,7 @@ jobs: restore-keys: cargo-${{ runner.os }}-reset20240331 - name: Install rustup-toolchain-install-master - if: ${{ steps.cache.outputs.cache-hit != 'true' }} + if: steps.cache.outputs.cache-hit != 'true' run: cargo install -f rustup-toolchain-install-master - name: Install "master" toolchain From f399ed23678ff15831588272cc1343a5398ff7be Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 00:03:23 +0200 Subject: [PATCH 134/208] make many-seeds a mode of ./miri run rather than a separate command --- ci/ci.sh | 13 +++-- miri-script/Cargo.lock | 8 ++-- miri-script/Cargo.toml | 2 +- miri-script/src/commands.rs | 96 ++++++++++++++++--------------------- miri-script/src/main.rs | 49 +++++++++++-------- miri-script/src/util.rs | 53 ++++++++++++++++++++ 6 files changed, 134 insertions(+), 87 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index c1ffb80783..0db267650d 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -71,10 +71,9 @@ function run_tests { time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic} fi if [ -n "${MANY_SEEDS-}" ]; then - # Also run some many-seeds tests. 64 seeds means this takes around a minute per test. - # (Need to invoke via explicit `bash -c` for Windows.) + # Also run some many-seeds tests. time for FILE in tests/many-seeds/*.rs; do - MIRI_SEEDS=$MANY_SEEDS ./miri many-seeds "$BASH" -c "./miri run '$FILE'" + ./miri run "--many-seeds=0..$MANY_SEEDS" "$FILE" done fi if [ -n "${TEST_BENCH-}" ]; then @@ -135,7 +134,7 @@ case $HOST_TARGET in GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 # With reduced many-seed count to avoid spending too much time on that. - # (All OSes are run with 64 seeds at least once though via the macOS runner.) + # (All OSes and ABIs are run with 64 seeds at least once though via the macOS runner.) MANY_SEEDS=16 MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests MANY_SEEDS=16 MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-apple-darwin run_tests @@ -164,9 +163,9 @@ case $HOST_TARGET in ;; i686-pc-windows-msvc) # Host - # Only smoke-test `many-seeds`; 64 runs of just the scoped-thread-leak test take 15min here! - # See . - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=1 TEST_BENCH=1 run_tests + # With reduced many-seeds count as this is the slowest runner already. + # (The macOS runner checks windows-msvc with full many-seeds count.) + GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=16 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, # and a 64bit target works on a 32bit host. diff --git a/miri-script/Cargo.lock b/miri-script/Cargo.lock index a6f7467f0a..5e792abac1 100644 --- a/miri-script/Cargo.lock +++ b/miri-script/Cargo.lock @@ -466,15 +466,15 @@ checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "xshell" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad" +checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e" +checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" diff --git a/miri-script/Cargo.toml b/miri-script/Cargo.toml index 79d0b13600..631d3a8210 100644 --- a/miri-script/Cargo.toml +++ b/miri-script/Cargo.toml @@ -19,7 +19,7 @@ itertools = "0.11" path_macro = "1.0" shell-words = "1.1" anyhow = "1.0" -xshell = "0.2" +xshell = "0.2.6" rustc_version = "0.4" dunce = "1.0.4" directories = "5" diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index b460c7eba5..7e34ad050b 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -2,6 +2,7 @@ use std::env; use std::ffi::OsString; use std::io::Write; use std::ops::Not; +use std::ops::Range; use std::path::PathBuf; use std::process; use std::thread; @@ -150,7 +151,6 @@ impl Command { | Command::Fmt { .. } | Command::Clippy { .. } | Command::Cargo { .. } => Self::auto_actions()?, - | Command::ManySeeds { .. } | Command::Toolchain { .. } | Command::Bench { .. } | Command::RustcPull { .. } @@ -162,11 +162,11 @@ impl Command { Command::Build { flags } => Self::build(flags), Command::Check { flags } => Self::check(flags), Command::Test { bless, flags } => Self::test(bless, flags), - Command::Run { dep, verbose, flags } => Self::run(dep, verbose, flags), + Command::Run { dep, verbose, many_seeds, flags } => + Self::run(dep, verbose, many_seeds, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { flags } => Self::clippy(flags), Command::Cargo { flags } => Self::cargo(flags), - Command::ManySeeds { command } => Self::many_seeds(command), Command::Bench { benches } => Self::bench(benches), Command::Toolchain { flags } => Self::toolchain(flags), Command::RustcPull { commit } => Self::rustc_pull(commit.clone()), @@ -367,43 +367,6 @@ impl Command { Ok(()) } - fn many_seeds(command: Vec) -> Result<()> { - let seed_start: u64 = env::var("MIRI_SEED_START") - .unwrap_or_else(|_| "0".into()) - .parse() - .context("failed to parse MIRI_SEED_START")?; - let seed_end: u64 = match (env::var("MIRI_SEEDS"), env::var("MIRI_SEED_END")) { - (Ok(_), Ok(_)) => bail!("Only one of MIRI_SEEDS and MIRI_SEED_END may be set"), - (Ok(seeds), Err(_)) => - seed_start + seeds.parse::().context("failed to parse MIRI_SEEDS")?, - (Err(_), Ok(seed_end)) => seed_end.parse().context("failed to parse MIRI_SEED_END")?, - (Err(_), Err(_)) => seed_start + 256, - }; - if seed_end <= seed_start { - bail!("the end of the seed range must be larger than the start."); - } - - let Some((command_name, trailing_args)) = command.split_first() else { - bail!("expected many-seeds command to be non-empty"); - }; - let sh = Shell::new()?; - sh.set_var("MIRI_AUTO_OPS", "no"); // just in case we get recursively invoked - for seed in seed_start..seed_end { - println!("Trying seed: {seed}"); - let mut miriflags = env::var_os("MIRIFLAGS").unwrap_or_default(); - miriflags.push(format!(" -Zlayout-seed={seed} -Zmiri-seed={seed}")); - let status = cmd!(sh, "{command_name} {trailing_args...}") - .env("MIRIFLAGS", miriflags) - .quiet() - .run(); - if let Err(err) = status { - println!("Failing seed: {seed}"); - return Err(err.into()); - } - } - Ok(()) - } - fn bench(benches: Vec) -> Result<()> { // The hyperfine to use let hyperfine = env::var("HYPERFINE"); @@ -495,7 +458,12 @@ impl Command { Ok(()) } - fn run(dep: bool, verbose: bool, mut flags: Vec) -> Result<()> { + fn run( + dep: bool, + verbose: bool, + many_seeds: Option>, + mut flags: Vec, + ) -> Result<()> { let mut e = MiriEnv::new()?; // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so // that we set the MIRI_SYSROOT up the right way. We must make sure that @@ -526,26 +494,46 @@ impl Command { flags.push("--sysroot".into()); flags.push(miri_sysroot.into()); - // Then run the actual command. Also add MIRIFLAGS. + // Compute everything needed to run the actual command. Also add MIRIFLAGS. let miri_manifest = path!(e.miri_dir / "Cargo.toml"); let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default(); let miri_flags = flagsplit(&miri_flags); let toolchain = &e.toolchain; let extra_flags = &e.cargo_extra_flags; let quiet_flag = if verbose { None } else { Some("--quiet") }; - let mut cmd = if dep { - cmd!( - e.sh, - "cargo +{toolchain} {quiet_flag...} test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode {miri_flags...} {flags...}" - ) - } else { - cmd!( - e.sh, - "cargo +{toolchain} {quiet_flag...} run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}" - ) + // This closure runs the command with the given `seed_flag` added between the MIRIFLAGS and + // the `flags` given on the command-line. + let run_miri = |sh: &Shell, seed_flag: Option| -> Result<()> { + // The basic command that executes the Miri driver. + let mut cmd = if dep { + cmd!( + sh, + "cargo +{toolchain} {quiet_flag...} test {extra_flags...} --manifest-path {miri_manifest} --test ui -- --miri-run-dep-mode" + ) + } else { + cmd!( + sh, + "cargo +{toolchain} {quiet_flag...} run {extra_flags...} --manifest-path {miri_manifest} --" + ) + }; + cmd.set_quiet(!verbose); + // Add Miri flags + let cmd = cmd.args(&miri_flags).args(seed_flag).args(&flags); + // And run the thing. + Ok(cmd.run()?) }; - cmd.set_quiet(!verbose); - cmd.run()?; + // Run the closure once or many times. + if let Some(seed_range) = many_seeds { + e.run_many_times(seed_range, |sh, seed| { + eprintln!("Trying seed: {seed}"); + run_miri(sh, Some(format!("-Zmiri-seed={seed}"))).map_err(|err| { + eprintln!("FAILING SEED: {seed}"); + err + }) + })?; + } else { + run_miri(&e.sh, None)?; + } Ok(()) } diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index 4904744cb9..f0ebbc8469 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -3,10 +3,10 @@ mod commands; mod util; -use std::env; use std::ffi::OsString; +use std::{env, ops::Range}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; #[derive(Clone, Debug)] pub enum Command { @@ -39,6 +39,7 @@ pub enum Command { Run { dep: bool, verbose: bool, + many_seeds: Option>, /// Flags that are passed through to `miri`. flags: Vec, }, @@ -55,10 +56,6 @@ pub enum Command { /// Runs just `cargo ` with the Miri-specific environment variables. /// Mainly meant to be invoked by rust-analyzer. Cargo { flags: Vec }, - /// Runs over and over again with different seeds for Miri. The MIRIFLAGS - /// variable is set to its original value appended with ` -Zmiri-seed=$SEED` for - /// many different seeds. - ManySeeds { command: Vec }, /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. Bench { /// List of benchmarks to run. By default all benchmarks are run. @@ -91,9 +88,11 @@ Just check miri. are passed to `cargo check`. Build miri, set up a sysroot and then run the test suite. are passed to the final `cargo test` invocation. -./miri run [--dep] [-v|--verbose] : +./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] : Build miri, set up a sysroot and then run the driver with the given . (Also respects MIRIFLAGS environment variable.) +If `--many-seeds` is present, Miri is run many times in parallel with different seeds. +The range defaults to `0..256`. ./miri fmt : Format all sources and tests. are passed to `rustfmt`. @@ -111,13 +110,6 @@ install`. Sets up the rpath such that the installed binary should work in any working directory. Note that the binaries are placed in the `miri` toolchain sysroot, to prevent conflicts with other toolchains. -./miri many-seeds : -Runs over and over again with different seeds for Miri. The MIRIFLAGS -variable is set to its original value appended with ` -Zmiri-seed=$SEED` for -many different seeds. MIRI_SEED_START controls the first seed to try (default: 0). -MIRI_SEEDS controls how many seeds are being tried (default: 256); -alternatively, MIRI_SEED_END controls the end of the (exclusive) seed range to try. - ./miri bench : Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. can explicitly list the benchmarks to run; by default, all of them are run. @@ -165,22 +157,37 @@ fn main() -> Result<()> { Some("run") => { let mut dep = false; let mut verbose = false; - while let Some(arg) = args.peek().and_then(|a| a.to_str()) { - match arg { - "--dep" => dep = true, - "-v" | "--verbose" => verbose = true, - _ => break, // not for us + let mut many_seeds = None; + while let Some(arg) = args.peek().and_then(|s| s.to_str()) { + if arg == "--dep" { + dep = true; + } else if arg == "-v" || arg == "--verbose" { + verbose = true; + } else if arg == "--many-seeds" { + many_seeds = Some(0..256); + } else if let Some(val) = arg.strip_prefix("--many-seeds=") { + let (from, to) = val.split_once("..").ok_or_else(|| { + anyhow!("invalid format for `--many-seeds`: expected `from..to`") + })?; + let from: u32 = if from.is_empty() { + 0 + } else { + from.parse().context("invalid `from` in `--many-seeds=from..to")? + }; + let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?; + many_seeds = Some(from..to); + } else { + break; // not for us } // Consume the flag, look at the next one. args.next().unwrap(); } - Command::Run { dep, verbose, flags: args.collect() } + Command::Run { dep, verbose, many_seeds, flags: args.collect() } } Some("fmt") => Command::Fmt { flags: args.collect() }, Some("clippy") => Command::Clippy { flags: args.collect() }, Some("cargo") => Command::Cargo { flags: args.collect() }, Some("install") => Command::Install { flags: args.collect() }, - Some("many-seeds") => Command::ManySeeds { command: args.collect() }, Some("bench") => Command::Bench { benches: args.collect() }, Some("toolchain") => Command::Toolchain { flags: args.collect() }, Some("rustc-pull") => { diff --git a/miri-script/src/util.rs b/miri-script/src/util.rs index 361a4ca0cf..23b5e936ed 100644 --- a/miri-script/src/util.rs +++ b/miri-script/src/util.rs @@ -1,5 +1,8 @@ use std::ffi::{OsStr, OsString}; +use std::ops::Range; use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use std::thread; use anyhow::{anyhow, Context, Result}; use dunce::canonicalize; @@ -189,4 +192,54 @@ impl MiriEnv { Ok(()) } + + /// Run the given closure many times in parallel with access to the shell, once for each value in the `range`. + pub fn run_many_times( + &self, + range: Range, + run: impl Fn(&Shell, u32) -> Result<()> + Sync, + ) -> Result<()> { + // `next` is atomic so threads can concurrently fetch their next value to run. + let next = AtomicU32::new(range.start); + let end = range.end; // exclusive! + let failed = AtomicBool::new(false); + thread::scope(|s| { + let mut handles = Vec::new(); + // Spawn one worker per core. + for _ in 0..thread::available_parallelism()?.get() { + // Create a copy of the shell for this thread. + let local_shell = self.sh.clone(); + let handle = s.spawn(|| -> Result<()> { + let local_shell = local_shell; // move the copy into this thread. + // Each worker thread keeps asking for numbers until we're all done. + loop { + let cur = next.fetch_add(1, Ordering::Relaxed); + if cur >= end { + // We hit the upper limit and are done. + break; + } + // Run the command with this seed. + run(&local_shell, cur).map_err(|err| { + // If we failed, tell everyone about this. + failed.store(true, Ordering::Relaxed); + err + })?; + // Check if some other command failed (in which case we'll stop as well). + if failed.load(Ordering::Relaxed) { + return Ok(()); + } + } + Ok(()) + }); + handles.push(handle); + } + // Wait for all workers to be done. + for handle in handles { + handle.join().unwrap()?; + } + // If all workers succeeded, we can't have failed. + assert!(!failed.load(Ordering::Relaxed)); + Ok(()) + }) + } } From 6b93ac6078d65cb7ea3810c06f41cd02c489c991 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 09:02:51 +0200 Subject: [PATCH 135/208] remove a hack that is no longer needed --- ci/ci.sh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index 0db267650d..c8c24ba5da 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -13,15 +13,6 @@ function endgroup { begingroup "Building Miri" -# Special Windows hacks -if [ "$HOST_TARGET" = i686-pc-windows-msvc ]; then - # The $BASH variable is `/bin/bash` here, but that path does not actually work. There are some - # hacks in place somewhere to try to paper over this, but the hacks dont work either (see - # ). So we hard-code the correct location for Github - # CI instead. - BASH="C:/Program Files/Git/usr/bin/bash" -fi - # Global configuration export RUSTFLAGS="-D warnings" export CARGO_INCREMENTAL=0 From 3ec749e83ae3cae52817b0e6966f79de3e0bfcba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 10:45:54 +0200 Subject: [PATCH 136/208] tls dtors: treat all unixes uniformly --- src/shims/tls.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/shims/tls.rs b/src/shims/tls.rs index d25bae1cdc..0dec12f0b6 100644 --- a/src/shims/tls.rs +++ b/src/shims/tls.rs @@ -242,21 +242,21 @@ impl<'tcx> TlsDtorsState<'tcx> { match &mut self.0 { Init => { match this.tcx.sess.target.os.as_ref() { - "linux" | "freebsd" | "android" => { - // Run the pthread dtors. - break 'new_state PthreadDtors(Default::default()); - } "macos" => { // The macOS thread wide destructor runs "before any TLS slots get // freed", so do that first. this.schedule_macos_tls_dtor()?; - // When the stack is empty again, go on with the pthread dtors. + // When that destructor is done, go on with the pthread dtors. + break 'new_state PthreadDtors(Default::default()); + } + _ if this.target_os_is_unix() => { + // All other Unixes directly jump to running the pthread dtors. break 'new_state PthreadDtors(Default::default()); } "windows" => { // Determine which destructors to run. let dtors = this.lookup_windows_tls_dtors()?; - // And move to the final state. + // And move to the next state, that runs them. break 'new_state WindowsDtors(dtors); } _ => { From 5e30f0fcef07e0ebdec31441f61d09a257ad78c9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 10:52:25 +0200 Subject: [PATCH 137/208] macos: use getentropy from libc --- tests/pass-dep/shims/libc-getentropy.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/pass-dep/shims/libc-getentropy.rs b/tests/pass-dep/shims/libc-getentropy.rs index 4b863f6851..06109397c2 100644 --- a/tests/pass-dep/shims/libc-getentropy.rs +++ b/tests/pass-dep/shims/libc-getentropy.rs @@ -1,12 +1,5 @@ //@ignore-target-windows: no libc -// on macOS this is not in the `libc` crate. -#[cfg(target_os = "macos")] -extern "C" { - fn getentropy(bytes: *mut libc::c_void, count: libc::size_t) -> libc::c_int; -} - -#[cfg(not(target_os = "macos"))] use libc::getentropy; fn main() { From 41b5a1b4e8294fc620bb3f4450aa01859325add0 Mon Sep 17 00:00:00 2001 From: Luv-Ray Date: Sat, 4 May 2024 17:24:18 +0800 Subject: [PATCH 138/208] Make file descriptors into refcount references take ownership of self and return `io::Result<()>` in `FileDescription::close` Co-authored-by: Ralf Jung --- src/shims/unix/fd.rs | 172 ++++++++++++++++---------------- src/shims/unix/fs.rs | 120 +++++++++++----------- src/shims/unix/linux/epoll.rs | 69 ++++++------- src/shims/unix/linux/eventfd.rs | 15 ++- src/shims/unix/mod.rs | 2 +- src/shims/unix/socket.rs | 16 ++- 6 files changed, 193 insertions(+), 201 deletions(-) diff --git a/src/shims/unix/fd.rs b/src/shims/unix/fd.rs index 18a41f6c66..566988cba1 100644 --- a/src/shims/unix/fd.rs +++ b/src/shims/unix/fd.rs @@ -2,8 +2,10 @@ //! standard file descriptors (stdin/stdout/stderr). use std::any::Any; +use std::cell::{Ref, RefCell, RefMut}; use std::collections::BTreeMap; use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write}; +use std::rc::Rc; use rustc_middle::ty::TyCtxt; use rustc_target::abi::Size; @@ -12,7 +14,7 @@ use crate::shims::unix::*; use crate::*; /// Represents an open file descriptor. -pub trait FileDescriptor: std::fmt::Debug + Any { +pub trait FileDescription: std::fmt::Debug + Any { fn name(&self) -> &'static str; fn read<'tcx>( @@ -44,13 +46,10 @@ pub trait FileDescriptor: std::fmt::Debug + Any { fn close<'tcx>( self: Box, _communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { + ) -> InterpResult<'tcx, io::Result<()>> { throw_unsup_format!("cannot close {}", self.name()); } - /// Return a new file descriptor *that refers to the same underlying object*. - fn dup(&mut self) -> io::Result>; - fn is_tty(&self, _communicate_allowed: bool) -> bool { // Most FDs are not tty's and the consequence of a wrong `false` are minor, // so we use a default impl here. @@ -58,7 +57,7 @@ pub trait FileDescriptor: std::fmt::Debug + Any { } } -impl dyn FileDescriptor { +impl dyn FileDescription { #[inline(always)] pub fn downcast_ref(&self) -> Option<&T> { (self as &dyn Any).downcast_ref() @@ -70,7 +69,7 @@ impl dyn FileDescriptor { } } -impl FileDescriptor for io::Stdin { +impl FileDescription for io::Stdin { fn name(&self) -> &'static str { "stdin" } @@ -88,16 +87,12 @@ impl FileDescriptor for io::Stdin { Ok(Read::read(self, bytes)) } - fn dup(&mut self) -> io::Result> { - Ok(Box::new(io::stdin())) - } - fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.is_terminal() } } -impl FileDescriptor for io::Stdout { +impl FileDescription for io::Stdout { fn name(&self) -> &'static str { "stdout" } @@ -120,16 +115,12 @@ impl FileDescriptor for io::Stdout { Ok(result) } - fn dup(&mut self) -> io::Result> { - Ok(Box::new(io::stdout())) - } - fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.is_terminal() } } -impl FileDescriptor for io::Stderr { +impl FileDescription for io::Stderr { fn name(&self) -> &'static str { "stderr" } @@ -145,10 +136,6 @@ impl FileDescriptor for io::Stderr { Ok(Write::write(&mut { self }, bytes)) } - fn dup(&mut self) -> io::Result> { - Ok(Box::new(io::stderr())) - } - fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.is_terminal() } @@ -158,7 +145,7 @@ impl FileDescriptor for io::Stderr { #[derive(Debug)] pub struct NullOutput; -impl FileDescriptor for NullOutput { +impl FileDescription for NullOutput { fn name(&self) -> &'static str { "stderr and stdout" } @@ -172,16 +159,30 @@ impl FileDescriptor for NullOutput { // We just don't write anything, but report to the user that we did. Ok(Ok(bytes.len())) } +} + +#[derive(Clone, Debug)] +pub struct FileDescriptor(Rc>>); + +impl FileDescriptor { + pub fn new(fd: T) -> Self { + FileDescriptor(Rc::new(RefCell::new(Box::new(fd)))) + } - fn dup(&mut self) -> io::Result> { - Ok(Box::new(NullOutput)) + pub fn close<'ctx>(self, communicate_allowed: bool) -> InterpResult<'ctx, io::Result<()>> { + // Destroy this `Rc` using `into_inner` so we can call `close` instead of + // implicitly running the destructor of the file description. + match Rc::into_inner(self.0) { + Some(fd) => RefCell::into_inner(fd).close(communicate_allowed), + None => Ok(Ok(())), + } } } /// The file descriptor table #[derive(Debug)] pub struct FdTable { - pub fds: BTreeMap>, + pub fds: BTreeMap, } impl VisitProvenance for FdTable { @@ -192,28 +193,24 @@ impl VisitProvenance for FdTable { impl FdTable { pub(crate) fn new(mute_stdout_stderr: bool) -> FdTable { - let mut fds: BTreeMap<_, Box> = BTreeMap::new(); - fds.insert(0i32, Box::new(io::stdin())); + let mut fds: BTreeMap<_, FileDescriptor> = BTreeMap::new(); + fds.insert(0i32, FileDescriptor::new(io::stdin())); if mute_stdout_stderr { - fds.insert(1i32, Box::new(NullOutput)); - fds.insert(2i32, Box::new(NullOutput)); + fds.insert(1i32, FileDescriptor::new(NullOutput)); + fds.insert(2i32, FileDescriptor::new(NullOutput)); } else { - fds.insert(1i32, Box::new(io::stdout())); - fds.insert(2i32, Box::new(io::stderr())); + fds.insert(1i32, FileDescriptor::new(io::stdout())); + fds.insert(2i32, FileDescriptor::new(io::stderr())); } FdTable { fds } } - pub fn insert_fd(&mut self, file_handle: Box) -> i32 { + pub fn insert_fd(&mut self, file_handle: FileDescriptor) -> i32 { self.insert_fd_with_min_fd(file_handle, 0) } /// Insert a new FD that is at least `min_fd`. - pub fn insert_fd_with_min_fd( - &mut self, - file_handle: Box, - min_fd: i32, - ) -> i32 { + pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32) -> i32 { // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in // between used FDs, the find_map combinator will return it. If the first such unused FD // is after all other used FDs, the find_map combinator will return None, and we will use @@ -239,15 +236,22 @@ impl FdTable { new_fd } - pub fn get(&self, fd: i32) -> Option<&dyn FileDescriptor> { - Some(&**self.fds.get(&fd)?) + pub fn get(&self, fd: i32) -> Option> { + let fd = self.fds.get(&fd)?; + Some(Ref::map(fd.0.borrow(), |fd| fd.as_ref())) } - pub fn get_mut(&mut self, fd: i32) -> Option<&mut dyn FileDescriptor> { - Some(&mut **self.fds.get_mut(&fd)?) + pub fn get_mut(&self, fd: i32) -> Option> { + let fd = self.fds.get(&fd)?; + Some(RefMut::map(fd.0.borrow_mut(), |fd| fd.as_mut())) } - pub fn remove(&mut self, fd: i32) -> Option> { + pub fn dup(&self, fd: i32) -> Option { + let fd = self.fds.get(&fd)?; + Some(fd.clone()) + } + + pub fn remove(&mut self, fd: i32) -> Option { self.fds.remove(&fd) } @@ -296,17 +300,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } let start = this.read_scalar(&args[2])?.to_i32()?; - match this.machine.fds.get_mut(fd) { - Some(file_descriptor) => { - let dup_result = file_descriptor.dup(); - match dup_result { - Ok(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)), - Err(e) => { - this.set_last_error_from_io_error(e.kind())?; - Ok(-1) - } - } - } + match this.machine.fds.dup(fd) { + Some(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)), None => this.fd_not_found(), } } else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") { @@ -330,6 +325,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.remove(fd) { let result = file_descriptor.close(this.machine.communicate())?; + // return `0` if close is successful + let result = result.map(|()| 0i32); this.try_unwrap_io_result(result)? } else { this.fd_not_found()? @@ -369,32 +366,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .min(u64::try_from(isize::MAX).unwrap()); let communicate = this.machine.communicate(); - if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { - trace!("read: FD mapped to {:?}", file_descriptor); - // We want to read at most `count` bytes. We are sure that `count` is not negative - // because it was a target's `usize`. Also we are sure that its smaller than - // `usize::MAX` because it is bounded by the host's `isize`. - let mut bytes = vec![0; usize::try_from(count).unwrap()]; - // `File::read` never returns a value larger than `count`, - // so this cannot fail. - let result = file_descriptor - .read(communicate, &mut bytes, *this.tcx)? - .map(|c| i64::try_from(c).unwrap()); - - match result { - Ok(read_bytes) => { - // If reading to `bytes` did not fail, we write those bytes to the buffer. - this.write_bytes_ptr(buf, bytes)?; - Ok(read_bytes) - } - Err(e) => { - this.set_last_error_from_io_error(e.kind())?; - Ok(-1) - } - } - } else { + let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { trace!("read: FD not found"); - this.fd_not_found() + return this.fd_not_found(); + }; + + trace!("read: FD mapped to {:?}", file_descriptor); + // We want to read at most `count` bytes. We are sure that `count` is not negative + // because it was a target's `usize`. Also we are sure that its smaller than + // `usize::MAX` because it is bounded by the host's `isize`. + let mut bytes = vec![0; usize::try_from(count).unwrap()]; + // `File::read` never returns a value larger than `count`, + // so this cannot fail. + let result = file_descriptor + .read(communicate, &mut bytes, *this.tcx)? + .map(|c| i64::try_from(c).unwrap()); + drop(file_descriptor); + + match result { + Ok(read_bytes) => { + // If reading to `bytes` did not fail, we write those bytes to the buffer. + this.write_bytes_ptr(buf, bytes)?; + Ok(read_bytes) + } + Err(e) => { + this.set_last_error_from_io_error(e.kind())?; + Ok(-1) + } } } @@ -419,13 +417,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let communicate = this.machine.communicate(); let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned(); - if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { - let result = file_descriptor - .write(communicate, &bytes, *this.tcx)? - .map(|c| i64::try_from(c).unwrap()); - this.try_unwrap_io_result(result) - } else { - this.fd_not_found() - } + let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { + return this.fd_not_found(); + }; + + let result = file_descriptor + .write(communicate, &bytes, *this.tcx)? + .map(|c| i64::try_from(c).unwrap()); + drop(file_descriptor); + + this.try_unwrap_io_result(result) } } diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 0bf0e3d52c..058747916c 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -17,15 +17,17 @@ use crate::shims::unix::*; use crate::*; use shims::time::system_time_to_duration; +use self::fd::FileDescriptor; + #[derive(Debug)] struct FileHandle { file: File, writable: bool, } -impl FileDescriptor for FileHandle { +impl FileDescription for FileHandle { fn name(&self) -> &'static str { - "FILE" + "file" } fn read<'tcx>( @@ -60,16 +62,14 @@ impl FileDescriptor for FileHandle { fn close<'tcx>( self: Box, communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { + ) -> InterpResult<'tcx, io::Result<()>> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); // We sync the file if it was opened in a mode different than read-only. if self.writable { // `File::sync_all` does the checks that are done when closing a file. We do this to // to handle possible errors correctly. - let result = self.file.sync_all().map(|_| 0i32); - // Now we actually close the file. - drop(self); - // And return the result. + let result = self.file.sync_all(); + // Now we actually close the file and return the result. Ok(result) } else { // We drop the file, this closes it but ignores any errors @@ -78,16 +78,10 @@ impl FileDescriptor for FileHandle { // `/dev/urandom` which are read-only. Check // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439 // for a deeper discussion. - drop(self); - Ok(Ok(0)) + Ok(Ok(())) } } - fn dup(&mut self) -> io::Result> { - let duplicated = self.file.try_clone()?; - Ok(Box::new(FileHandle { file: duplicated, writable: self.writable })) - } - fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.file.is_terminal() } @@ -399,7 +393,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let fd = options.open(path).map(|file| { let fh = &mut this.machine.fds; - fh.insert_fd(Box::new(FileHandle { file, writable })) + fh.insert_fd(FileDescriptor::new(FileHandle { file, writable })) }); this.try_unwrap_io_result(fd) @@ -428,14 +422,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; let communicate = this.machine.communicate(); - Ok(Scalar::from_i64(if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { - let result = file_descriptor - .seek(communicate, seek_from)? - .map(|offset| i64::try_from(offset).unwrap()); - this.try_unwrap_io_result(result)? - } else { - this.fd_not_found()? - })) + + let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else { + return Ok(Scalar::from_i64(this.fd_not_found()?)); + }; + let result = file_descriptor + .seek(communicate, seek_from)? + .map(|offset| i64::try_from(offset).unwrap()); + drop(file_descriptor); + + let result = this.try_unwrap_io_result(result)?; + Ok(Scalar::from_i64(result)) } fn unlink(&mut self, path_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { @@ -1131,32 +1128,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(Scalar::from_i32(this.fd_not_found()?)); } - Ok(Scalar::from_i32(if let Some(file_descriptor) = this.machine.fds.get_mut(fd) { - // FIXME: Support ftruncate64 for all FDs - let FileHandle { file, writable } = - file_descriptor.downcast_ref::().ok_or_else(|| { - err_unsup_format!( - "`ftruncate64` is only supported on file-backed file descriptors" - ) - })?; - if *writable { - if let Ok(length) = length.try_into() { - let result = file.set_len(length); - this.try_unwrap_io_result(result.map(|_| 0i32))? - } else { - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - -1 - } + let Some(file_descriptor) = this.machine.fds.get(fd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); + }; + + // FIXME: Support ftruncate64 for all FDs + let FileHandle { file, writable } = + file_descriptor.downcast_ref::().ok_or_else(|| { + err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors") + })?; + + if *writable { + if let Ok(length) = length.try_into() { + let result = file.set_len(length); + drop(file_descriptor); + let result = this.try_unwrap_io_result(result.map(|_| 0i32))?; + Ok(Scalar::from_i32(result)) } else { - // The file is not writable + drop(file_descriptor); let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; - -1 + Ok(Scalar::from_i32(-1)) } } else { - this.fd_not_found()? - })) + drop(file_descriptor); + // The file is not writable + let einval = this.eval_libc("EINVAL"); + this.set_last_error(einval)?; + Ok(Scalar::from_i32(-1)) + } } fn fsync(&mut self, fd_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { @@ -1190,6 +1190,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { err_unsup_format!("`fsync` is only supported on file-backed file descriptors") })?; let io_result = maybe_sync_file(file, *writable, File::sync_all); + drop(file_descriptor); this.try_unwrap_io_result(io_result) } @@ -1214,6 +1215,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors") })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); + drop(file_descriptor); this.try_unwrap_io_result(io_result) } @@ -1263,6 +1265,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) })?; let io_result = maybe_sync_file(file, *writable, File::sync_data); + drop(file_descriptor); Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?)) } @@ -1498,7 +1501,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match file { Ok(f) => { let fh = &mut this.machine.fds; - let fd = fh.insert_fd(Box::new(FileHandle { file: f, writable: true })); + let fd = + fh.insert_fd(FileDescriptor::new(FileHandle { file: f, writable: true })); return Ok(fd); } Err(e) => @@ -1563,21 +1567,21 @@ impl FileMetadata { ecx: &mut MiriInterpCx<'_, 'tcx>, fd: i32, ) -> InterpResult<'tcx, Option> { - let option = ecx.machine.fds.get(fd); - let file = match option { - Some(file_descriptor) => - &file_descriptor - .downcast_ref::() - .ok_or_else(|| { - err_unsup_format!( - "obtaining metadata is only supported on file-backed file descriptors" - ) - })? - .file, - None => return ecx.fd_not_found().map(|_: i32| None), + let Some(file_descriptor) = ecx.machine.fds.get(fd) else { + return ecx.fd_not_found().map(|_: i32| None); }; - let metadata = file.metadata(); + let file = &file_descriptor + .downcast_ref::() + .ok_or_else(|| { + err_unsup_format!( + "obtaining metadata is only supported on file-backed file descriptors" + ) + })? + .file; + + let metadata = file.metadata(); + drop(file_descriptor); FileMetadata::from_meta(ecx, metadata) } diff --git a/src/shims/unix/linux/epoll.rs b/src/shims/unix/linux/epoll.rs index 5161d91ca3..48a0ba0197 100644 --- a/src/shims/unix/linux/epoll.rs +++ b/src/shims/unix/linux/epoll.rs @@ -5,6 +5,8 @@ use rustc_data_structures::fx::FxHashMap; use crate::shims::unix::*; use crate::*; +use self::shims::unix::fd::FileDescriptor; + /// An `Epoll` file descriptor connects file handles and epoll events #[derive(Clone, Debug, Default)] struct Epoll { @@ -29,22 +31,16 @@ struct EpollEvent { data: Scalar, } -impl FileDescriptor for Epoll { +impl FileDescription for Epoll { fn name(&self) -> &'static str { "epoll" } - fn dup(&mut self) -> io::Result> { - // FIXME: this is probably wrong -- check if the `dup`ed descriptor truly uses an - // independent event set. - Ok(Box::new(self.clone())) - } - fn close<'tcx>( self: Box, _communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { - Ok(Ok(0)) + ) -> InterpResult<'tcx, io::Result<()>> { + Ok(Ok(())) } } @@ -70,7 +66,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("epoll_create1 flags {flags} are not implemented"); } - let fd = this.machine.fds.insert_fd(Box::new(Epoll::default())); + let fd = this.machine.fds.insert_fd(FileDescriptor::new(Epoll::default())); Ok(Scalar::from_i32(fd)) } @@ -114,27 +110,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let data = this.read_scalar(&data)?; let event = EpollEvent { events, data }; - if let Some(epfd) = this.machine.fds.get_mut(epfd) { - let epfd = epfd - .downcast_mut::() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; + let Some(mut epfd) = this.machine.fds.get_mut(epfd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); + }; + let epfd = epfd + .downcast_mut::() + .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; - epfd.file_descriptors.insert(fd, event); - Ok(Scalar::from_i32(0)) - } else { - Ok(Scalar::from_i32(this.fd_not_found()?)) - } + epfd.file_descriptors.insert(fd, event); + Ok(Scalar::from_i32(0)) } else if op == epoll_ctl_del { - if let Some(epfd) = this.machine.fds.get_mut(epfd) { - let epfd = epfd - .downcast_mut::() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; - - epfd.file_descriptors.remove(&fd); - Ok(Scalar::from_i32(0)) - } else { - Ok(Scalar::from_i32(this.fd_not_found()?)) - } + let Some(mut epfd) = this.machine.fds.get_mut(epfd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); + }; + let epfd = epfd + .downcast_mut::() + .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_ctl`"))?; + + epfd.file_descriptors.remove(&fd); + Ok(Scalar::from_i32(0)) } else { let einval = this.eval_libc("EINVAL"); this.set_last_error(einval)?; @@ -185,15 +179,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let _maxevents = this.read_scalar(maxevents)?.to_i32()?; let _timeout = this.read_scalar(timeout)?.to_i32()?; - if let Some(epfd) = this.machine.fds.get_mut(epfd) { - let _epfd = epfd - .downcast_mut::() - .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; + let Some(mut epfd) = this.machine.fds.get_mut(epfd) else { + return Ok(Scalar::from_i32(this.fd_not_found()?)); + }; + let _epfd = epfd + .downcast_mut::() + .ok_or_else(|| err_unsup_format!("non-epoll FD passed to `epoll_wait`"))?; - // FIXME return number of events ready when scheme for marking events ready exists - throw_unsup_format!("returning ready events from epoll_wait is not yet implemented"); - } else { - Ok(Scalar::from_i32(this.fd_not_found()?)) - } + // FIXME return number of events ready when scheme for marking events ready exists + throw_unsup_format!("returning ready events from epoll_wait is not yet implemented"); } } diff --git a/src/shims/unix/linux/eventfd.rs b/src/shims/unix/linux/eventfd.rs index 452527017f..a865f2efff 100644 --- a/src/shims/unix/linux/eventfd.rs +++ b/src/shims/unix/linux/eventfd.rs @@ -8,6 +8,8 @@ use rustc_target::abi::Endian; use crate::shims::unix::*; use crate::*; +use self::shims::unix::fd::FileDescriptor; + /// A kind of file descriptor created by `eventfd`. /// The `Event` type isn't currently written to by `eventfd`. /// The interface is meant to keep track of objects associated @@ -22,21 +24,16 @@ struct Event { val: u64, } -impl FileDescriptor for Event { +impl FileDescription for Event { fn name(&self) -> &'static str { "event" } - fn dup(&mut self) -> io::Result> { - // FIXME: this is wrong, the new and old FD should refer to the same event object! - Ok(Box::new(Event { val: self.val })) - } - fn close<'tcx>( self: Box, _communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { - Ok(Ok(0)) + ) -> InterpResult<'tcx, io::Result<()>> { + Ok(Ok(())) } /// A write call adds the 8-byte integer value supplied in @@ -115,7 +112,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("eventfd: EFD_SEMAPHORE is unsupported"); } - let fd = this.machine.fds.insert_fd(Box::new(Event { val: val.into() })); + let fd = this.machine.fds.insert_fd(FileDescriptor::new(Event { val: val.into() })); Ok(Scalar::from_i32(fd)) } } diff --git a/src/shims/unix/mod.rs b/src/shims/unix/mod.rs index 144593aa2f..bede2fbd38 100644 --- a/src/shims/unix/mod.rs +++ b/src/shims/unix/mod.rs @@ -13,7 +13,7 @@ mod linux; mod macos; pub use env::UnixEnvVars; -pub use fd::{FdTable, FileDescriptor}; +pub use fd::{FdTable, FileDescription}; pub use fs::DirTable; // All the Unix-specific extension traits pub use env::EvalContextExt as _; diff --git a/src/shims/unix/socket.rs b/src/shims/unix/socket.rs index 84ddd746fb..11fd83f57e 100644 --- a/src/shims/unix/socket.rs +++ b/src/shims/unix/socket.rs @@ -3,26 +3,24 @@ use std::io; use crate::shims::unix::*; use crate::*; +use self::fd::FileDescriptor; + /// Pair of connected sockets. /// /// We currently don't allow sending any data through this pair, so this can be just a dummy. #[derive(Debug)] struct SocketPair; -impl FileDescriptor for SocketPair { +impl FileDescription for SocketPair { fn name(&self) -> &'static str { "socketpair" } - fn dup(&mut self) -> io::Result> { - Ok(Box::new(SocketPair)) - } - fn close<'tcx>( self: Box, _communicate_allowed: bool, - ) -> InterpResult<'tcx, io::Result> { - Ok(Ok(0)) + ) -> InterpResult<'tcx, io::Result<()>> { + Ok(Ok(())) } } @@ -52,9 +50,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // FIXME: fail on unsupported inputs let fds = &mut this.machine.fds; - let sv0 = fds.insert_fd(Box::new(SocketPair)); + let sv0 = fds.insert_fd(FileDescriptor::new(SocketPair)); let sv0 = Scalar::try_from_int(sv0, sv.layout.size).unwrap(); - let sv1 = fds.insert_fd(Box::new(SocketPair)); + let sv1 = fds.insert_fd(FileDescriptor::new(SocketPair)); let sv1 = Scalar::try_from_int(sv1, sv.layout.size).unwrap(); this.write_scalar(sv0, &sv)?; From ad3e2610f10a2a3947b773f1194e688dc233b59d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 10:58:52 +0200 Subject: [PATCH 139/208] speed up Windows runner: don't run GC_STRESS test --- ci/ci.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index c8c24ba5da..6945b39032 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -154,9 +154,9 @@ case $HOST_TARGET in ;; i686-pc-windows-msvc) # Host - # With reduced many-seeds count as this is the slowest runner already. + # Without GC_STRESS and with reduced many-seeds count as this is the slowest runner. # (The macOS runner checks windows-msvc with full many-seeds count.) - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=16 TEST_BENCH=1 run_tests + MIR_OPT=1 MANY_SEEDS=16 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, # and a 64bit target works on a 32bit host. From 68a55bc677e469b59619c973532d269d1164a00a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 11:46:10 +0200 Subject: [PATCH 140/208] show time taken in run_tests_minimal --- ci/ci.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index 6945b39032..3962138635 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -107,10 +107,10 @@ function run_tests_minimal { exit 1 fi - ./miri test -- "$@" + time ./miri test -- "$@" # Ensure that a small smoke test of cargo-miri works. - cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml --target ${MIRI_TEST_TARGET-$HOST_TARGET} + time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml --target ${MIRI_TEST_TARGET-$HOST_TARGET} endgroup } From 590ab9b6db1e8aeae5c7c813e8fd8a20b1f51744 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 11:55:17 +0200 Subject: [PATCH 141/208] move available-parallelism tests into shims/ folder --- tests/pass/{ => shims}/available-parallelism-miri-num-cpus.rs | 0 tests/pass/{ => shims}/available-parallelism.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/pass/{ => shims}/available-parallelism-miri-num-cpus.rs (100%) rename tests/pass/{ => shims}/available-parallelism.rs (100%) diff --git a/tests/pass/available-parallelism-miri-num-cpus.rs b/tests/pass/shims/available-parallelism-miri-num-cpus.rs similarity index 100% rename from tests/pass/available-parallelism-miri-num-cpus.rs rename to tests/pass/shims/available-parallelism-miri-num-cpus.rs diff --git a/tests/pass/available-parallelism.rs b/tests/pass/shims/available-parallelism.rs similarity index 100% rename from tests/pass/available-parallelism.rs rename to tests/pass/shims/available-parallelism.rs From e6d4b0e56a1d071859c7b0dd9da3513f4d647363 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 12:29:36 +0200 Subject: [PATCH 142/208] rename integer test --- tests/pass/{ints.rs => integers.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/pass/{ints.rs => integers.rs} (100%) diff --git a/tests/pass/ints.rs b/tests/pass/integers.rs similarity index 100% rename from tests/pass/ints.rs rename to tests/pass/integers.rs From eeb3d1f10abac17b3e0c077a20fba211e5805718 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 12:34:16 +0200 Subject: [PATCH 143/208] move intrinsics implementations and tests into dedicated folder and make them separate from 'shims' --- src/{shims => }/intrinsics/atomic.rs | 0 src/{shims => }/intrinsics/mod.rs | 0 src/{shims => }/intrinsics/simd.rs | 0 src/lib.rs | 3 ++- src/shims/mod.rs | 1 - tests/fail/{shims => intrinsics}/intrinsic_target_feature.rs | 0 .../fail/{shims => intrinsics}/intrinsic_target_feature.stderr | 0 tests/pass/{intrinsics-integer.rs => intrinsics/integer.rs} | 0 tests/pass/{ => intrinsics}/intrinsics.rs | 0 tests/pass/{ => intrinsics}/portable-simd-ptrs.rs | 0 tests/pass/{ => intrinsics}/portable-simd.rs | 0 tests/pass/{ => intrinsics}/volatile.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-aes-vaes.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-avx.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-avx2.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-avx512.rs | 0 .../pass/{ => shims/x86}/intrinsics-x86-pause-without-sse2.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-sse.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-sse2.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-sse3-ssse3.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86-sse41.rs | 0 tests/pass/{ => shims/x86}/intrinsics-x86.rs | 0 22 files changed, 2 insertions(+), 2 deletions(-) rename src/{shims => }/intrinsics/atomic.rs (100%) rename src/{shims => }/intrinsics/mod.rs (100%) rename src/{shims => }/intrinsics/simd.rs (100%) rename tests/fail/{shims => intrinsics}/intrinsic_target_feature.rs (100%) rename tests/fail/{shims => intrinsics}/intrinsic_target_feature.stderr (100%) rename tests/pass/{intrinsics-integer.rs => intrinsics/integer.rs} (100%) rename tests/pass/{ => intrinsics}/intrinsics.rs (100%) rename tests/pass/{ => intrinsics}/portable-simd-ptrs.rs (100%) rename tests/pass/{ => intrinsics}/portable-simd.rs (100%) rename tests/pass/{ => intrinsics}/volatile.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-aes-vaes.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-avx.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-avx2.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-avx512.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-pause-without-sse2.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-sse.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-sse2.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-sse3-ssse3.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86-sse41.rs (100%) rename tests/pass/{ => shims/x86}/intrinsics-x86.rs (100%) diff --git a/src/shims/intrinsics/atomic.rs b/src/intrinsics/atomic.rs similarity index 100% rename from src/shims/intrinsics/atomic.rs rename to src/intrinsics/atomic.rs diff --git a/src/shims/intrinsics/mod.rs b/src/intrinsics/mod.rs similarity index 100% rename from src/shims/intrinsics/mod.rs rename to src/intrinsics/mod.rs diff --git a/src/shims/intrinsics/simd.rs b/src/intrinsics/simd.rs similarity index 100% rename from src/shims/intrinsics/simd.rs rename to src/intrinsics/simd.rs diff --git a/src/lib.rs b/src/lib.rs index f47c84842b..f01c2da25d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,6 +79,7 @@ mod concurrency; mod diagnostics; mod eval; mod helpers; +mod intrinsics; mod machine; mod mono_hash_map; mod operator; @@ -95,9 +96,9 @@ pub use rustc_const_eval::interpret::*; #[doc(no_inline)] pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _}; +pub use crate::intrinsics::EvalContextExt as _; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; -pub use crate::shims::intrinsics::EvalContextExt as _; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 85c9a202f7..ab5a8c5877 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -5,7 +5,6 @@ mod backtrace; #[cfg(target_os = "linux")] pub mod ffi_support; pub mod foreign_items; -pub mod intrinsics; pub mod unix; pub mod windows; mod x86; diff --git a/tests/fail/shims/intrinsic_target_feature.rs b/tests/fail/intrinsics/intrinsic_target_feature.rs similarity index 100% rename from tests/fail/shims/intrinsic_target_feature.rs rename to tests/fail/intrinsics/intrinsic_target_feature.rs diff --git a/tests/fail/shims/intrinsic_target_feature.stderr b/tests/fail/intrinsics/intrinsic_target_feature.stderr similarity index 100% rename from tests/fail/shims/intrinsic_target_feature.stderr rename to tests/fail/intrinsics/intrinsic_target_feature.stderr diff --git a/tests/pass/intrinsics-integer.rs b/tests/pass/intrinsics/integer.rs similarity index 100% rename from tests/pass/intrinsics-integer.rs rename to tests/pass/intrinsics/integer.rs diff --git a/tests/pass/intrinsics.rs b/tests/pass/intrinsics/intrinsics.rs similarity index 100% rename from tests/pass/intrinsics.rs rename to tests/pass/intrinsics/intrinsics.rs diff --git a/tests/pass/portable-simd-ptrs.rs b/tests/pass/intrinsics/portable-simd-ptrs.rs similarity index 100% rename from tests/pass/portable-simd-ptrs.rs rename to tests/pass/intrinsics/portable-simd-ptrs.rs diff --git a/tests/pass/portable-simd.rs b/tests/pass/intrinsics/portable-simd.rs similarity index 100% rename from tests/pass/portable-simd.rs rename to tests/pass/intrinsics/portable-simd.rs diff --git a/tests/pass/volatile.rs b/tests/pass/intrinsics/volatile.rs similarity index 100% rename from tests/pass/volatile.rs rename to tests/pass/intrinsics/volatile.rs diff --git a/tests/pass/intrinsics-x86-aes-vaes.rs b/tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs similarity index 100% rename from tests/pass/intrinsics-x86-aes-vaes.rs rename to tests/pass/shims/x86/intrinsics-x86-aes-vaes.rs diff --git a/tests/pass/intrinsics-x86-avx.rs b/tests/pass/shims/x86/intrinsics-x86-avx.rs similarity index 100% rename from tests/pass/intrinsics-x86-avx.rs rename to tests/pass/shims/x86/intrinsics-x86-avx.rs diff --git a/tests/pass/intrinsics-x86-avx2.rs b/tests/pass/shims/x86/intrinsics-x86-avx2.rs similarity index 100% rename from tests/pass/intrinsics-x86-avx2.rs rename to tests/pass/shims/x86/intrinsics-x86-avx2.rs diff --git a/tests/pass/intrinsics-x86-avx512.rs b/tests/pass/shims/x86/intrinsics-x86-avx512.rs similarity index 100% rename from tests/pass/intrinsics-x86-avx512.rs rename to tests/pass/shims/x86/intrinsics-x86-avx512.rs diff --git a/tests/pass/intrinsics-x86-pause-without-sse2.rs b/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs similarity index 100% rename from tests/pass/intrinsics-x86-pause-without-sse2.rs rename to tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs diff --git a/tests/pass/intrinsics-x86-sse.rs b/tests/pass/shims/x86/intrinsics-x86-sse.rs similarity index 100% rename from tests/pass/intrinsics-x86-sse.rs rename to tests/pass/shims/x86/intrinsics-x86-sse.rs diff --git a/tests/pass/intrinsics-x86-sse2.rs b/tests/pass/shims/x86/intrinsics-x86-sse2.rs similarity index 100% rename from tests/pass/intrinsics-x86-sse2.rs rename to tests/pass/shims/x86/intrinsics-x86-sse2.rs diff --git a/tests/pass/intrinsics-x86-sse3-ssse3.rs b/tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs similarity index 100% rename from tests/pass/intrinsics-x86-sse3-ssse3.rs rename to tests/pass/shims/x86/intrinsics-x86-sse3-ssse3.rs diff --git a/tests/pass/intrinsics-x86-sse41.rs b/tests/pass/shims/x86/intrinsics-x86-sse41.rs similarity index 100% rename from tests/pass/intrinsics-x86-sse41.rs rename to tests/pass/shims/x86/intrinsics-x86-sse41.rs diff --git a/tests/pass/intrinsics-x86.rs b/tests/pass/shims/x86/intrinsics-x86.rs similarity index 100% rename from tests/pass/intrinsics-x86.rs rename to tests/pass/shims/x86/intrinsics-x86.rs From ad43c6bd52815d9bb151837c6532690a1137c415 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 12:39:37 +0200 Subject: [PATCH 144/208] fix grouping of target-specific LLVM shims --- src/shims/foreign_items.rs | 60 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 4b96ff18b7..6a12180b83 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -893,36 +893,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty); } } - // FIXME: Move these to an `arm` submodule. - "llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => { - let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; - let arg = this.read_scalar(arg)?.to_i32()?; - match arg { - // SY ("full system scope") - 15 => { - this.yield_active_thread(); - } - _ => { - throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg); - } - } - } - "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => { - let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; - let arg = this.read_scalar(arg)?.to_i32()?; - // Note that different arguments might have different target feature requirements. - match arg { - // YIELD - 1 => { - this.expect_target_feature_for_intrinsic(link_name, "v6")?; - this.yield_active_thread(); - } - _ => { - throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg); - } - } - } - // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm // `{i,u}8x16_popcnt` functions. name if name.starts_with("llvm.ctpop.v") => { @@ -946,6 +916,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + // Target-specific shims name if name.starts_with("llvm.x86.") && (this.tcx.sess.target.arch == "x86" || this.tcx.sess.target.arch == "x86_64") => @@ -954,6 +925,35 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this, link_name, abi, args, dest, ); } + // FIXME: Move these to an `arm` submodule. + "llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => { + let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; + let arg = this.read_scalar(arg)?.to_i32()?; + match arg { + // SY ("full system scope") + 15 => { + this.yield_active_thread(); + } + _ => { + throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg); + } + } + } + "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => { + let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; + let arg = this.read_scalar(arg)?.to_i32()?; + // Note that different arguments might have different target feature requirements. + match arg { + // YIELD + 1 => { + this.expect_target_feature_for_intrinsic(link_name, "v6")?; + this.yield_active_thread(); + } + _ => { + throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg); + } + } + } // Platform-specific shims _ => From 7590c8b98c4ecdf6c6de2bc50630146bbf632570 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 10:56:53 +0200 Subject: [PATCH 145/208] move some minimal targets over to the macOS runner, to even out CI times --- ci/ci.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index 3962138635..67835960bd 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -130,9 +130,16 @@ case $HOST_TARGET in MANY_SEEDS=16 MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-apple-darwin run_tests MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests + ;; + aarch64-apple-darwin) + # Host (tier 2) + GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + # Extra tier 1 + MANY_SEEDS=64 MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests + MANY_SEEDS=64 MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests # Extra tier 2 - MIRI_TEST_TARGET=aarch64-apple-darwin run_tests MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests + MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Partially supported targets (tier 2) MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus @@ -143,15 +150,6 @@ case $HOST_TARGET in # Custom target JSON file MIRI_TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std ;; - aarch64-apple-darwin) - # Host (tier 2) - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests - # Extra tier 1 - MANY_SEEDS=64 MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MANY_SEEDS=64 MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests - # Extra tier 2 - MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture - ;; i686-pc-windows-msvc) # Host # Without GC_STRESS and with reduced many-seeds count as this is the slowest runner. From ad60e8b6340ac72d51adaa175483e6379b6bbc57 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 13:33:12 +0200 Subject: [PATCH 146/208] Revert "moving out sched_getaffinity interception from linux'shim, FreeBSD supporting it too." This reverts commit c1a3f8576ea12b0bed68ad3dedf4069ca3d9816f. --- src/shims/unix/foreign_items.rs | 19 ------------------- src/shims/unix/linux/foreign_items.rs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index bd299aaa12..b07f596c74 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -724,25 +724,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - "sched_getaffinity" => { - // FreeBSD supports it as well since 13.1 (as a wrapper of cpuset_getaffinity) - if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd") { - throw_unsup_format!( - "`sched_getaffinity` is not supported on {}", - this.tcx.sess.target.os - ); - } - let [pid, cpusetsize, mask] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.read_scalar(pid)?.to_i32()?; - this.read_target_usize(cpusetsize)?; - this.deref_pointer_as(mask, this.libc_ty_layout("cpu_set_t"))?; - // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`. - let einval = this.eval_libc("EINVAL"); - this.set_last_error(einval)?; - this.write_scalar(Scalar::from_i32(-1), dest)?; - } - // Platform-specific shims _ => { let target_os = &*this.tcx.sess.target.os; diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 497e8bee70..0d4c7ce112 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -195,6 +195,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?; } + "sched_getaffinity" => { + // This shim isn't useful, aside from the fact that it makes `num_cpus` + // fall back to `sysconf` where it will successfully determine the number of CPUs. + let [pid, cpusetsize, mask] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + this.read_scalar(pid)?.to_i32()?; + this.read_target_usize(cpusetsize)?; + this.deref_pointer_as(mask, this.libc_ty_layout("cpu_set_t"))?; + // FIXME: we just return an error. + let einval = this.eval_libc("EINVAL"); + this.set_last_error(einval)?; + this.write_scalar(Scalar::from_i32(-1), dest)?; + } // 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. From 23be20b205251b9277cf5e44917b0190f386aeb0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 13:45:54 +0200 Subject: [PATCH 147/208] update 'unsupported' message --- README.md | 2 +- src/diagnostics.rs | 5 ++++- tests/extern-so/fail/function_not_in_so.stderr | 3 ++- tests/fail-dep/shims/fs/close_stdout.stderr | 3 ++- tests/fail-dep/shims/fs/read_from_stdout.stderr | 3 ++- tests/fail-dep/shims/fs/write_to_stdin.stderr | 3 ++- tests/fail-dep/tokio/sleep.stderr | 3 ++- tests/fail-dep/unsupported_incomplete_function.stderr | 3 ++- tests/fail/alloc/no_global_allocator.stderr | 3 ++- tests/fail/extern-type-field-offset.stderr | 3 ++- tests/fail/extern_static.stderr | 3 ++- tests/fail/extern_static_in_const.stderr | 3 ++- tests/fail/extern_static_wrong_size.stderr | 3 ++- ...sue-miri-3288-ice-symbolic-alignment-extern-static.stderr | 3 ++- tests/fail/shims/backtrace/bad-backtrace-flags.stderr | 3 ++- .../fail/shims/backtrace/bad-backtrace-resolve-flags.stderr | 3 ++- .../shims/backtrace/bad-backtrace-resolve-names-flags.stderr | 3 ++- tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr | 3 ++- .../promise_alignment.call_unaligned_ptr.stderr | 3 ++- tests/fail/unaligned_pointers/promise_alignment_zero.stderr | 3 ++- tests/fail/unsized-local.stderr | 3 ++- tests/fail/unsupported_foreign_function.stderr | 3 ++- 22 files changed, 45 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ef01ca25fb..5b088abcbe 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ interpreter will explicitly tell you when it finds something unsupported: error: unsupported operation: can't call foreign function: bind ... = help: this is likely not a bug in the program; it indicates that the program \ - performed an operation that the interpreter does not support + performed an operation that Miri does not support ``` ### Cross-interpretation: running for different targets diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 9fa786332e..5fac922f56 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -320,7 +320,10 @@ pub fn report_error<'tcx, 'mir>( #[rustfmt::skip] let helps = match e.kind() { Unsupported(_) => - vec![(None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support"))], + vec![ + (None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support")), + (None, format!("if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there")), + ], UndefinedBehavior(AlignmentCheckFailed { .. }) if ecx.machine.check_alignment == AlignmentCheck::Symbolic => diff --git a/tests/extern-so/fail/function_not_in_so.stderr b/tests/extern-so/fail/function_not_in_so.stderr index c031cb0146..26761c4872 100644 --- a/tests/extern-so/fail/function_not_in_so.stderr +++ b/tests/extern-so/fail/function_not_in_so.stderr @@ -4,7 +4,8 @@ error: unsupported operation: can't call foreign function `foo` on $OS LL | foo(); | ^^^^^ can't call foreign function `foo` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/function_not_in_so.rs:LL:CC diff --git a/tests/fail-dep/shims/fs/close_stdout.stderr b/tests/fail-dep/shims/fs/close_stdout.stderr index 7547ec4171..e504801576 100644 --- a/tests/fail-dep/shims/fs/close_stdout.stderr +++ b/tests/fail-dep/shims/fs/close_stdout.stderr @@ -4,7 +4,8 @@ error: unsupported operation: cannot close stdout LL | libc::close(1); | ^^^^^^^^^^^^^^ cannot close stdout | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/close_stdout.rs:LL:CC diff --git a/tests/fail-dep/shims/fs/read_from_stdout.stderr b/tests/fail-dep/shims/fs/read_from_stdout.stderr index 355e16d5c3..fb0fb8acc3 100644 --- a/tests/fail-dep/shims/fs/read_from_stdout.stderr +++ b/tests/fail-dep/shims/fs/read_from_stdout.stderr @@ -4,7 +4,8 @@ error: unsupported operation: cannot read from stdout LL | libc::read(1, bytes.as_mut_ptr() as *mut libc::c_void, 512); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot read from stdout | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/read_from_stdout.rs:LL:CC diff --git a/tests/fail-dep/shims/fs/write_to_stdin.stderr b/tests/fail-dep/shims/fs/write_to_stdin.stderr index e2ebe234b0..8426c5cb84 100644 --- a/tests/fail-dep/shims/fs/write_to_stdin.stderr +++ b/tests/fail-dep/shims/fs/write_to_stdin.stderr @@ -4,7 +4,8 @@ error: unsupported operation: cannot write to stdin LL | libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot write to stdin | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/write_to_stdin.rs:LL:CC diff --git a/tests/fail-dep/tokio/sleep.stderr b/tests/fail-dep/tokio/sleep.stderr index 59179478c3..f7d829f77d 100644 --- a/tests/fail-dep/tokio/sleep.stderr +++ b/tests/fail-dep/tokio/sleep.stderr @@ -9,7 +9,8 @@ LL | | timeout, LL | | )) | |__________^ returning ready events from epoll_wait is not yet implemented | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there error: aborting due to 1 previous error diff --git a/tests/fail-dep/unsupported_incomplete_function.stderr b/tests/fail-dep/unsupported_incomplete_function.stderr index fabe3bbf12..23327ff7db 100644 --- a/tests/fail-dep/unsupported_incomplete_function.stderr +++ b/tests/fail-dep/unsupported_incomplete_function.stderr @@ -4,7 +4,8 @@ error: unsupported operation: can't call foreign function `signal` on $OS LL | libc::signal(libc::SIGPIPE, libc::SIG_IGN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function `signal` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/unsupported_incomplete_function.rs:LL:CC diff --git a/tests/fail/alloc/no_global_allocator.stderr b/tests/fail/alloc/no_global_allocator.stderr index bd2a6c628f..70f60a3f59 100644 --- a/tests/fail/alloc/no_global_allocator.stderr +++ b/tests/fail/alloc/no_global_allocator.stderr @@ -4,7 +4,8 @@ error: unsupported operation: can't call foreign function `__rust_alloc` on $OS LL | __rust_alloc(1, 1); | ^^^^^^^^^^^^^^^^^^ can't call foreign function `__rust_alloc` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `start` at $DIR/no_global_allocator.rs:LL:CC diff --git a/tests/fail/extern-type-field-offset.stderr b/tests/fail/extern-type-field-offset.stderr index cbbf1b3836..14609ec699 100644 --- a/tests/fail/extern-type-field-offset.stderr +++ b/tests/fail/extern-type-field-offset.stderr @@ -4,7 +4,8 @@ error: unsupported operation: `extern type` does not have a known offset LL | let _field = &x.a; | ^^^^ `extern type` does not have a known offset | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern-type-field-offset.rs:LL:CC diff --git a/tests/fail/extern_static.stderr b/tests/fail/extern_static.stderr index 34bb273f04..1d12fd76be 100644 --- a/tests/fail/extern_static.stderr +++ b/tests/fail/extern_static.stderr @@ -4,7 +4,8 @@ error: unsupported operation: extern static `FOO` is not supported by Miri LL | let _val = unsafe { std::ptr::addr_of!(FOO) }; | ^^^ extern static `FOO` is not supported by Miri | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern_static.rs:LL:CC diff --git a/tests/fail/extern_static_in_const.stderr b/tests/fail/extern_static_in_const.stderr index 45d1632cce..455d6af18e 100644 --- a/tests/fail/extern_static_in_const.stderr +++ b/tests/fail/extern_static_in_const.stderr @@ -4,7 +4,8 @@ error: unsupported operation: extern static `E` is not supported by Miri LL | let _val = X; | ^ extern static `E` is not supported by Miri | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern_static_in_const.rs:LL:CC diff --git a/tests/fail/extern_static_wrong_size.stderr b/tests/fail/extern_static_wrong_size.stderr index 27699d780c..91afb936fa 100644 --- a/tests/fail/extern_static_wrong_size.stderr +++ b/tests/fail/extern_static_wrong_size.stderr @@ -4,7 +4,8 @@ error: unsupported operation: extern static `environ` has been declared as `exte LL | let _val = unsafe { environ }; | ^^^^^^^ extern static `environ` has been declared as `extern_static_wrong_size::environ` with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern_static_wrong_size.rs:LL:CC diff --git a/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr b/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr index e5dbe74988..0d10b03fc4 100644 --- a/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr +++ b/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr @@ -4,7 +4,8 @@ error: unsupported operation: extern static `_dispatch_queue_attr_concurrent` is LL | let _val = *DISPATCH_QUEUE_CONCURRENT; | ^^^^^^^^^^^^^^^^^^^^^^^^^ extern static `_dispatch_queue_attr_concurrent` is not supported by Miri | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/issue-miri-3288-ice-symbolic-alignment-extern-static.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-flags.stderr index 2523e5063e..ced44ee776 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-flags.stderr @@ -4,7 +4,8 @@ error: unsupported operation: unknown `miri_get_backtrace` flags 2 LL | miri_get_backtrace(2, std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_get_backtrace` flags 2 | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-flags.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr index 36446ae283..6e282ff490 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr @@ -4,7 +4,8 @@ error: unsupported operation: unknown `miri_resolve_frame` flags 2 LL | miri_resolve_frame(buf[0], 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_resolve_frame` flags 2 | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-resolve-flags.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr index ff60ab0917..ddf8d221cc 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr @@ -4,7 +4,8 @@ error: unsupported operation: unknown `miri_resolve_frame_names` flags 2 LL | ... miri_resolve_frame_names(buf[0], 2, std::ptr::null_mut(), std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_resolve_frame_names` flags 2 | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-resolve-names-flags.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr index 6d4c370050..b3186fc5b0 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr @@ -4,7 +4,8 @@ error: unsupported operation: unknown `miri_backtrace_size` flags 2 LL | miri_backtrace_size(2); | ^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_backtrace_size` flags 2 | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-size-flags.rs:LL:CC diff --git a/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr b/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr index e23ac5ac2f..9ad715f7ba 100644 --- a/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr +++ b/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr @@ -4,7 +4,8 @@ error: unsupported operation: `miri_promise_symbolic_alignment`: pointer is not LL | unsafe { utils::miri_promise_symbolic_alignment(align8.add(1).cast(), 8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: pointer is not actually aligned | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/promise_alignment.rs:LL:CC diff --git a/tests/fail/unaligned_pointers/promise_alignment_zero.stderr b/tests/fail/unaligned_pointers/promise_alignment_zero.stderr index b3327e0493..62b6e85231 100644 --- a/tests/fail/unaligned_pointers/promise_alignment_zero.stderr +++ b/tests/fail/unaligned_pointers/promise_alignment_zero.stderr @@ -4,7 +4,8 @@ error: unsupported operation: `miri_promise_symbolic_alignment`: alignment must LL | unsafe { utils::miri_promise_symbolic_alignment(buffer.as_ptr().cast(), 0) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: alignment must be a power of 2, got 0 | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/promise_alignment_zero.rs:LL:CC diff --git a/tests/fail/unsized-local.stderr b/tests/fail/unsized-local.stderr index 0ffda9e6d6..d938f36bd5 100644 --- a/tests/fail/unsized-local.stderr +++ b/tests/fail/unsized-local.stderr @@ -4,7 +4,8 @@ error: unsupported operation: unsized locals are not supported LL | let x = *(Box::new(A) as Box); | ^ unsized locals are not supported | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/unsized-local.rs:LL:CC diff --git a/tests/fail/unsupported_foreign_function.stderr b/tests/fail/unsupported_foreign_function.stderr index 5a2f415bb8..7492fdd77b 100644 --- a/tests/fail/unsupported_foreign_function.stderr +++ b/tests/fail/unsupported_foreign_function.stderr @@ -4,7 +4,8 @@ error: unsupported operation: can't call foreign function `foo` on $OS LL | foo(); | ^^^^^ can't call foreign function `foo` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/unsupported_foreign_function.rs:LL:CC From 4d44b111485e5ad3dde77347364ee6465ee36ff8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 11:48:16 +0200 Subject: [PATCH 148/208] freebsd: test std threadname and fs APIs also reorder foreign_items to fix the grouping, and reorder the tests_minimal invocations to be more consistent --- ci/ci.sh | 12 ++++--- src/shims/unix/freebsd/foreign_items.rs | 32 +++++++++---------- .../{thread_name.rs => threadname.rs} | 0 3 files changed, 23 insertions(+), 21 deletions(-) rename tests/pass/concurrency/{thread_name.rs => threadname.rs} (100%) diff --git a/ci/ci.sh b/ci/ci.sh index 67835960bd..d3dee0de61 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -141,11 +141,13 @@ case $HOST_TARGET in MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Partially supported targets (tier 2) - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus - MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal hello integer vec panic/panic concurrency/simple pthread-threadname libc-getentropy libc-getrandom libc-misc libc-fs atomic env align num_cpus - MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal hello integer vec panic/panic - MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal no_std integer strings wasm - MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std integer strings wasm + VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims + BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus + MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus + MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic + MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm + MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std # Custom target JSON file MIRI_TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index ffb583123d..3db9b9c1db 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -47,21 +47,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.read_scalar(len)?, )?; } - "getrandom" => { - let [ptr, len, flags] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.read_pointer(ptr)?; - let len = this.read_target_usize(len)?; - let _flags = this.read_scalar(flags)?.to_i32()?; - // flags on freebsd does not really matter - // in practice, GRND_RANDOM does not particularly draw from /dev/random - // since it is the same as to /dev/urandom. - // GRND_INSECURE is only an alias of GRND_NONBLOCK, which - // does not affect the RNG. - // https://man.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&n=1 - this.gen_random(ptr, len)?; - this.write_scalar(Scalar::from_target_usize(len, this), dest)?; - } // File related shims // For those, we both intercept `func` and `call@FBSD_1.0` symbols cases @@ -90,7 +75,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(result, dest)?; } - // errno + // Miscellaneous + "getrandom" => { + let [ptr, len, flags] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let len = this.read_target_usize(len)?; + let _flags = this.read_scalar(flags)?.to_i32()?; + // flags on freebsd does not really matter + // in practice, GRND_RANDOM does not particularly draw from /dev/random + // since it is the same as to /dev/urandom. + // GRND_INSECURE is only an alias of GRND_NONBLOCK, which + // does not affect the RNG. + // https://man.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&n=1 + this.gen_random(ptr, len)?; + this.write_scalar(Scalar::from_target_usize(len, this), dest)?; + } "__error" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let errno_place = this.last_error_place()?; diff --git a/tests/pass/concurrency/thread_name.rs b/tests/pass/concurrency/threadname.rs similarity index 100% rename from tests/pass/concurrency/thread_name.rs rename to tests/pass/concurrency/threadname.rs From a36c45b224ebb61fc34f2874e88237378f0e8371 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 11:49:24 +0200 Subject: [PATCH 149/208] document unofficially supported OSes --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ef01ca25fb..616426bd83 100644 --- a/README.md +++ b/README.md @@ -224,8 +224,11 @@ degree documented below): - `s390x-unknown-linux-gnu` is supported as our "big-endian target of choice". - For every other target with OS `linux`, `macos`, or `windows`, Miri should generally work, but we make no promises and we don't run tests for such targets. -- For targets on other operating systems, even basic operations such as printing to the standard - output might not work, and Miri might fail before even reaching the `main` function. +- We have unofficial support (not maintained by the Miri team itself) for some further operating systems. + - `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. + - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. + - `wasm`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. +- For targets on other operating systems, Miri might fail before even reaching the `main` function. However, even for targets that we do support, the degree of support for accessing platform APIs (such as the file system) differs between targets: generally, Linux targets have the best support, From 67bca02d6a63c4ed023b1e34b2a9892c32486ea3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 13:00:59 +0200 Subject: [PATCH 150/208] make some tests not need output (so they work on wasm) --- tests/pass/align_offset_symbolic.rs | 5 ++++- tests/pass/align_offset_symbolic.stdout | 1 - tests/pass/vecdeque.rs | 4 ++-- tests/pass/vecdeque.stack.stdout | 2 -- tests/pass/vecdeque.tree.stdout | 2 -- tests/pass/vecdeque.tree_uniq.stdout | 2 -- 6 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 tests/pass/align_offset_symbolic.stdout delete mode 100644 tests/pass/vecdeque.stack.stdout delete mode 100644 tests/pass/vecdeque.tree.stdout delete mode 100644 tests/pass/vecdeque.tree_uniq.stdout diff --git a/tests/pass/align_offset_symbolic.rs b/tests/pass/align_offset_symbolic.rs index c32fa2c8f9..dec3d779a7 100644 --- a/tests/pass/align_offset_symbolic.rs +++ b/tests/pass/align_offset_symbolic.rs @@ -62,7 +62,10 @@ fn test_from_utf8() { const N: usize = 10; let vec = vec![0x4141414141414141u64; N]; let content = unsafe { std::slice::from_raw_parts(vec.as_ptr() as *const u8, 8 * N) }; - println!("{:?}", std::str::from_utf8(content).unwrap()); + assert_eq!( + std::str::from_utf8(content).unwrap(), + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + ); } fn test_u64_array() { diff --git a/tests/pass/align_offset_symbolic.stdout b/tests/pass/align_offset_symbolic.stdout deleted file mode 100644 index 66d4399481..0000000000 --- a/tests/pass/align_offset_symbolic.stdout +++ /dev/null @@ -1 +0,0 @@ -"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" diff --git a/tests/pass/vecdeque.rs b/tests/pass/vecdeque.rs index ccecf3d30a..77c4ca5a04 100644 --- a/tests/pass/vecdeque.rs +++ b/tests/pass/vecdeque.rs @@ -31,8 +31,8 @@ fn main() { } // Regression test for Debug impl's - println!("{:?} {:?}", dst, dst.iter()); - println!("{:?}", VecDeque::::new().iter()); + format!("{:?} {:?}", dst, dst.iter()); + format!("{:?}", VecDeque::::new().iter()); for a in dst { assert_eq!(*a, 2); diff --git a/tests/pass/vecdeque.stack.stdout b/tests/pass/vecdeque.stack.stdout deleted file mode 100644 index 63de960ee2..0000000000 --- a/tests/pass/vecdeque.stack.stdout +++ /dev/null @@ -1,2 +0,0 @@ -[2, 2] Iter([2, 2], []) -Iter([], []) diff --git a/tests/pass/vecdeque.tree.stdout b/tests/pass/vecdeque.tree.stdout deleted file mode 100644 index 63de960ee2..0000000000 --- a/tests/pass/vecdeque.tree.stdout +++ /dev/null @@ -1,2 +0,0 @@ -[2, 2] Iter([2, 2], []) -Iter([], []) diff --git a/tests/pass/vecdeque.tree_uniq.stdout b/tests/pass/vecdeque.tree_uniq.stdout deleted file mode 100644 index 63de960ee2..0000000000 --- a/tests/pass/vecdeque.tree_uniq.stdout +++ /dev/null @@ -1,2 +0,0 @@ -[2, 2] Iter([2, 2], []) -Iter([], []) From 2e6bf8b2c275e9960df6b3ebeed84d5b27452638 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 14:26:11 +0200 Subject: [PATCH 151/208] add helper function to declare an extern static for a weak symbol --- src/shims/extern_static.rs | 22 ++++++++++++++++------ src/shims/foreign_items.rs | 9 +++++++++ src/shims/unix/foreign_items.rs | 2 +- src/shims/unix/linux/foreign_items.rs | 2 -- src/shims/windows/foreign_items.rs | 2 +- 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/shims/extern_static.rs b/src/shims/extern_static.rs index 442338a611..5cec51e960 100644 --- a/src/shims/extern_static.rs +++ b/src/shims/extern_static.rs @@ -29,6 +29,21 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { Ok(()) } + /// Extern statics that are initialized with function pointers to the symbols of the same name. + fn weak_symbol_extern_statics( + this: &mut MiriInterpCx<'mir, 'tcx>, + names: &[&str], + ) -> InterpResult<'tcx> { + for name in names { + assert!(this.is_dyn_sym(name), "{name} is not a dynamic symbol"); + let layout = this.machine.layouts.const_raw_ptr; + let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name))); + let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout); + Self::alloc_extern_static(this, name, val)?; + } + Ok(()) + } + /// Sets up the "extern statics" for this machine. pub fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { // "__rust_no_alloc_shim_is_unstable" @@ -58,12 +73,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { } "android" => { Self::null_ptr_extern_statics(this, &["bsd_signal"])?; - // "signal" -- just needs a non-zero pointer value (function does not even get called), - // but we arrange for this to call the `signal` function anyway. - let layout = this.machine.layouts.const_raw_ptr; - let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str("signal"))); - let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout); - Self::alloc_extern_static(this, "signal", val)?; + Self::weak_symbol_extern_statics(this, &["signal"])?; } "windows" => { // "_tls_used" diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 6a12180b83..4f8bb80110 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -151,6 +151,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(None) } + fn is_dyn_sym(&self, name: &str) -> bool { + let this = self.eval_context_ref(); + match this.tcx.sess.target.os.as_ref() { + os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os), + "windows" => shims::windows::foreign_items::is_dyn_sym(name), + _ => false, + } + } + /// Emulates a call to a `DynSym`. fn emulate_dyn_sym( &mut self, diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index bd299aaa12..bebf8b4b8a 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -15,7 +15,7 @@ use shims::unix::freebsd::foreign_items as freebsd; use shims::unix::linux::foreign_items as linux; use shims::unix::macos::foreign_items as macos; -fn is_dyn_sym(name: &str, target_os: &str) -> bool { +pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { match name { // Used for tests. "isatty" => true, diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 497e8bee70..58ffd5fc1e 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -113,9 +113,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // have the right type. let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?; - let sys_statx = this.eval_libc("SYS_statx").to_target_usize(this)?; - let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?; if args.is_empty() { diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index e8ae80261c..7b156ee311 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -15,7 +15,7 @@ use crate::*; use shims::foreign_items::EmulateForeignItemResult; use shims::windows::handle::{Handle, PseudoHandle}; -fn is_dyn_sym(name: &str) -> bool { +pub fn is_dyn_sym(name: &str) -> bool { // std does dynamic detection for these symbols matches!( name, From 9e755ccfd22a4265620a1b4bb28a82a254a5b87c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 14:41:12 +0200 Subject: [PATCH 152/208] make statx a regular function (so we don't need to support the syscall) --- src/shims/extern_static.rs | 7 ++++--- src/shims/unix/fd.rs | 4 ++++ src/shims/unix/linux/foreign_items.rs | 25 ++++++++----------------- tests/pass/alloc-access-tracking.rs | 5 +++-- tests/pass/alloc-access-tracking.stderr | 8 ++++---- 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/shims/extern_static.rs b/src/shims/extern_static.rs index 5cec51e960..c3c7ef7c1f 100644 --- a/src/shims/extern_static.rs +++ b/src/shims/extern_static.rs @@ -16,8 +16,8 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { /// Zero-initialized pointer-sized extern statics are pretty common. /// Most of them are for weak symbols, which we all set to null (indicating that the - /// symbol is not supported, and triggering fallback code which ends up calling a - /// syscall that we do support). + /// symbol is not supported, and triggering fallback code which ends up calling + /// some other shim that we do support). fn null_ptr_extern_statics( this: &mut MiriInterpCx<'mir, 'tcx>, names: &[&str], @@ -59,8 +59,9 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { "linux" => { Self::null_ptr_extern_statics( this, - &["__cxa_thread_atexit_impl", "getrandom", "statx", "__clock_gettime64"], + &["__cxa_thread_atexit_impl", "__clock_gettime64"], )?; + Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?; // "environ" let environ = this.machine.env_vars.unix().environ(); Self::add_extern_static(this, "environ", environ); diff --git a/src/shims/unix/fd.rs b/src/shims/unix/fd.rs index 566988cba1..e536b78d1c 100644 --- a/src/shims/unix/fd.rs +++ b/src/shims/unix/fd.rs @@ -17,6 +17,7 @@ use crate::*; pub trait FileDescription: std::fmt::Debug + Any { fn name(&self) -> &'static str; + /// Reads as much as possible into the given buffer, and returns the number of bytes read. fn read<'tcx>( &mut self, _communicate_allowed: bool, @@ -26,6 +27,7 @@ pub trait FileDescription: std::fmt::Debug + Any { throw_unsup_format!("cannot read from {}", self.name()); } + /// Writes as much as possible from the given buffer, and returns the number of bytes written. fn write<'tcx>( &mut self, _communicate_allowed: bool, @@ -35,6 +37,8 @@ pub trait FileDescription: std::fmt::Debug + Any { throw_unsup_format!("cannot write to {}", self.name()); } + /// Seeks to the given offset (which can be relative to the beginning, end, or current position). + /// Returns the new position from the start of the stream. fn seek<'tcx>( &mut self, _communicate_allowed: bool, diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 58ffd5fc1e..71cdc3be81 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -12,7 +12,7 @@ use shims::unix::linux::mem::EvalContextExt as _; use shims::unix::linux::sync::futex; pub fn is_dyn_sym(name: &str) -> bool { - matches!(name, "getrandom") + matches!(name, "getrandom" | "statx") } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -29,7 +29,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. match link_name.as_str() { - // File related shims (but also see "syscall" below for statx) + // File related shims "readdir64" => { let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.linux_readdir64(dirp)?; @@ -41,6 +41,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.sync_file_range(fd, offset, nbytes, flags)?; this.write_scalar(result, dest)?; } + "statx" => { + let [dirfd, pathname, flags, mask, statxbuf] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; + this.write_scalar(Scalar::from_i32(result), dest)?; + } // epoll, eventfd "epoll_create1" => { @@ -113,7 +119,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // have the right type. let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?; - let sys_statx = this.eval_libc("SYS_statx").to_target_usize(this)?; let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?; if args.is_empty() { @@ -134,20 +139,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } getrandom(this, &args[1], &args[2], &args[3], dest)?; } - // `statx` is used by `libstd` to retrieve metadata information on `linux` - // instead of using `stat`,`lstat` or `fstat` as on `macos`. - id if id == sys_statx => { - // The first argument is the syscall id, so skip over it. - if args.len() < 6 { - throw_ub_format!( - "incorrect number of arguments for `statx` syscall: got {}, expected at least 6", - args.len() - ); - } - let result = - this.linux_statx(&args[1], &args[2], &args[3], &args[4], &args[5])?; - this.write_scalar(Scalar::from_target_isize(result.into(), this), dest)?; - } // `futex` is used by some synchronization primitives. id if id == sys_futex => { futex(this, &args[1..], dest)?; diff --git a/tests/pass/alloc-access-tracking.rs b/tests/pass/alloc-access-tracking.rs index 29c1ee2f7b..a226783155 100644 --- a/tests/pass/alloc-access-tracking.rs +++ b/tests/pass/alloc-access-tracking.rs @@ -1,7 +1,8 @@ #![feature(start)] #![no_std] -//@compile-flags: -Zmiri-track-alloc-id=18 -Zmiri-track-alloc-accesses -Cpanic=abort -//@only-target-linux: alloc IDs differ between OSes for some reason +//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort +//@normalize-stderr-test: "id 20" -> "id $$ALLOC" +//@only-target-linux: alloc IDs differ between OSes (due to extern static allocations) extern "Rust" { fn miri_alloc(size: usize, align: usize) -> *mut u8; diff --git a/tests/pass/alloc-access-tracking.stderr b/tests/pass/alloc-access-tracking.stderr index bef13701ea..0af6cde833 100644 --- a/tests/pass/alloc-access-tracking.stderr +++ b/tests/pass/alloc-access-tracking.stderr @@ -2,7 +2,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | let ptr = miri_alloc(123, 1); - | ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id 18 + | ^^^^^^^^^^^^^^^^^^ created Miri bare-metal heap allocation of 123 bytes (alignment ALIGN bytes) with id $ALLOC | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC @@ -11,7 +11,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | *ptr = 42; // Crucially, only a write is printed here, no read! - | ^^^^^^^^^ write access to allocation with id 18 + | ^^^^^^^^^ write access to allocation with id $ALLOC | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC @@ -20,7 +20,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | assert_eq!(*ptr, 42); - | ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id 18 + | ^^^^^^^^^^^^^^^^^^^^ read access to allocation with id $ALLOC | = note: BACKTRACE: = note: inside `start` at RUSTLIB/core/src/macros/mod.rs:LL:CC @@ -30,7 +30,7 @@ note: tracking was triggered --> $DIR/alloc-access-tracking.rs:LL:CC | LL | miri_dealloc(ptr, 123, 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id 18 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ freed allocation with id $ALLOC | = note: BACKTRACE: = note: inside `start` at $DIR/alloc-access-tracking.rs:LL:CC From 2a66882dd0df95272a691f7facde92827714c4a3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 17:39:29 +0200 Subject: [PATCH 153/208] interpret, miri: uniform treatments of intrinsics/functions with and without return block --- src/lib.rs | 1 + src/shims/alloc.rs | 9 +- src/shims/foreign_items.rs | 166 ++++++++++-------------- src/shims/intrinsics/atomic.rs | 6 +- src/shims/intrinsics/mod.rs | 91 +++++++------ src/shims/intrinsics/simd.rs | 6 +- src/shims/mod.rs | 10 ++ src/shims/panic.rs | 8 +- src/shims/unix/foreign_items.rs | 7 +- src/shims/unix/freebsd/foreign_items.rs | 7 +- src/shims/unix/linux/foreign_items.rs | 9 +- src/shims/unix/macos/foreign_items.rs | 7 +- src/shims/windows/foreign_items.rs | 7 +- src/shims/x86/aesni.rs | 7 +- src/shims/x86/avx.rs | 7 +- src/shims/x86/avx2.rs | 7 +- src/shims/x86/mod.rs | 11 +- src/shims/x86/sse.rs | 7 +- src/shims/x86/sse2.rs | 7 +- src/shims/x86/sse3.rs | 7 +- src/shims/x86/sse41.rs | 7 +- src/shims/x86/ssse3.rs | 7 +- 22 files changed, 184 insertions(+), 217 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f47c84842b..3b1d7687d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,6 +95,7 @@ pub use rustc_const_eval::interpret::*; #[doc(no_inline)] pub use rustc_const_eval::interpret::{self, AllocMap, PlaceTy, Provenance as _}; +pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; pub use crate::shims::intrinsics::EvalContextExt as _; diff --git a/src/shims/alloc.rs b/src/shims/alloc.rs index b5ae06c2a4..79531598c0 100644 --- a/src/shims/alloc.rs +++ b/src/shims/alloc.rs @@ -4,7 +4,6 @@ use rustc_ast::expand::allocator::AllocatorKind; use rustc_target::abi::{Align, Size}; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; /// Check some basic requirements for this allocation request: /// non-zero size, power-of-two alignment. @@ -55,12 +54,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn emulate_allocator( &mut self, default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); let Some(allocator_kind) = this.tcx.allocator_kind(()) else { // in real code, this symbol does not exist without an allocator - return Ok(EmulateForeignItemResult::NotSupported); + return Ok(EmulateItemResult::NotSupported); }; match allocator_kind { @@ -70,11 +69,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // and not execute any Miri shim. Somewhat unintuitively doing so is done // by returning `NotSupported`, which triggers the `lookup_exported_symbol` // fallback case in `emulate_foreign_item`. - return Ok(EmulateForeignItemResult::NotSupported); + return Ok(EmulateItemResult::NotSupported); } AllocatorKind::Default => { default(this)?; - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 636361148a..8a38c6c11a 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -28,16 +28,6 @@ impl DynSym { } } -/// Returned by `emulate_foreign_item_inner`. -pub enum EmulateForeignItemResult { - /// The caller is expected to jump to the return block. - NeedsJumping, - /// Jumping has already been taken care of. - AlreadyJumped, - /// The item is not supported. - NotSupported, -} - impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Emulates calling a foreign item, failing if the item is not supported. @@ -58,84 +48,47 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let tcx = this.tcx.tcx; - // First: functions that diverge. - let ret = match ret { - None => - match link_name.as_str() { - "miri_start_unwind" => { - // `check_shim` happens inside `handle_miri_start_unwind`. - this.handle_miri_start_unwind(abi, link_name, args, unwind)?; - return Ok(None); - } - // This matches calls to the foreign item `panic_impl`. - // The implementation is provided by the function with the `#[panic_handler]` attribute. - "panic_impl" => { - // We don't use `check_shim` here because we are just forwarding to the lang - // item. Argument count checking will be performed when the returned `Body` is - // called. - this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?; - let panic_impl_id = tcx.lang_items().panic_impl().unwrap(); - let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id); - return Ok(Some(( - this.load_mir(panic_impl_instance.def, None)?, - panic_impl_instance, - ))); - } - "__rust_alloc_error_handler" => { - // Forward to the right symbol that implements this function. - let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { - // in real code, this symbol does not exist without an allocator - throw_unsup_format!( - "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" - ); - }; - let name = alloc_error_handler_name(handler_kind); - let handler = this - .lookup_exported_symbol(Symbol::intern(name))? - .expect("missing alloc error handler symbol"); - return Ok(Some(handler)); - } - #[rustfmt::skip] - | "exit" - | "ExitProcess" - => { - let exp_abi = if link_name.as_str() == "exit" { - Abi::C { unwind: false } - } else { - Abi::System { unwind: false } - }; - let [code] = this.check_shim(abi, exp_abi, link_name, args)?; - // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway - let code = this.read_scalar(code)?.to_i32()?; - throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); - } - "abort" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - throw_machine_stop!(TerminationInfo::Abort( - "the program aborted execution".to_owned() - )) - } - _ => { - if let Some(body) = this.lookup_exported_symbol(link_name)? { - return Ok(Some(body)); - } - this.handle_unsupported(format!( - "can't call (diverging) foreign function: {link_name}" - ))?; - return Ok(None); - } - }, - Some(p) => p, - }; + // Some shims forward to other MIR bodies. + match link_name.as_str() { + // This matches calls to the foreign item `panic_impl`. + // The implementation is provided by the function with the `#[panic_handler]` attribute. + "panic_impl" => { + // We don't use `check_shim` here because we are just forwarding to the lang + // item. Argument count checking will be performed when the returned `Body` is + // called. + this.check_abi_and_shim_symbol_clash(abi, Abi::Rust, link_name)?; + let panic_impl_id = tcx.lang_items().panic_impl().unwrap(); + let panic_impl_instance = ty::Instance::mono(tcx, panic_impl_id); + return Ok(Some(( + this.load_mir(panic_impl_instance.def, None)?, + panic_impl_instance, + ))); + } + "__rust_alloc_error_handler" => { + // Forward to the right symbol that implements this function. + let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else { + // in real code, this symbol does not exist without an allocator + throw_unsup_format!( + "`__rust_alloc_error_handler` cannot be called when no alloc error handler is set" + ); + }; + let name = alloc_error_handler_name(handler_kind); + let handler = this + .lookup_exported_symbol(Symbol::intern(name))? + .expect("missing alloc error handler symbol"); + return Ok(Some(handler)); + } + _ => {} + } - // Second: functions that return immediately. - match this.emulate_foreign_item_inner(link_name, abi, args, dest)? { - EmulateForeignItemResult::NeedsJumping => { + // The rest either implements the logic, or falls back to `lookup_exported_symbol`. + match this.emulate_foreign_item_inner(link_name, abi, args, dest, unwind)? { + EmulateItemResult::NeedsJumping => { trace!("{:?}", this.dump_place(&dest.clone().into())); - this.go_to_block(ret); + this.return_to_block(ret)?; } - EmulateForeignItemResult::AlreadyJumped => (), - EmulateForeignItemResult::NotSupported => { + EmulateItemResult::AlreadyJumped => (), + EmulateItemResult::NotSupported => { if let Some(body) = this.lookup_exported_symbol(link_name)? { return Ok(Some(body)); } @@ -243,7 +196,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + unwind: mir::UnwindAction, + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // First deal with any external C functions in linked .so file. @@ -254,7 +208,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. if this.call_external_c_fct(link_name, dest, args)? { - return Ok(EmulateForeignItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsJumping); } } @@ -298,6 +252,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // shim, add it to the corresponding submodule. match link_name.as_str() { // Miri-specific extern functions + "miri_start_unwind" => { + // `check_shim` happens inside `handle_miri_start_unwind`. + this.handle_miri_start_unwind(abi, link_name, args, unwind)?; + return Ok(EmulateItemResult::AlreadyJumped); + } "miri_run_provenance_gc" => { let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; this.run_provenance_gc(); @@ -362,29 +321,24 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Return value: 0 on success, otherwise the size it would have needed. this.write_int(if success { 0 } else { needed_size }, dest)?; } - // Obtains the size of a Miri backtrace. See the README for details. "miri_backtrace_size" => { this.handle_miri_backtrace_size(abi, link_name, args, dest)?; } - // Obtains a Miri backtrace. See the README for details. "miri_get_backtrace" => { // `check_shim` happens inside `handle_miri_get_backtrace`. this.handle_miri_get_backtrace(abi, link_name, args, dest)?; } - // Resolves a Miri backtrace frame. See the README for details. "miri_resolve_frame" => { // `check_shim` happens inside `handle_miri_resolve_frame`. this.handle_miri_resolve_frame(abi, link_name, args, dest)?; } - // Writes the function and file names of a Miri backtrace frame into a user provided buffer. See the README for details. "miri_resolve_frame_names" => { this.handle_miri_resolve_frame_names(abi, link_name, args)?; } - // Writes some bytes to the interpreter's stdout/stderr. See the // README for details. "miri_write_to_stdout" | "miri_write_to_stderr" => { @@ -398,7 +352,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => unreachable!(), }; } - // Promises that a pointer has a given symbolic alignment. "miri_promise_symbolic_alignment" => { use rustc_target::abi::AlignFromBytesError; @@ -442,6 +395,25 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } + // Aborting the process. + "exit" | "ExitProcess" => { + let exp_abi = if link_name.as_str() == "exit" { + Abi::C { unwind: false } + } else { + Abi::System { unwind: false } + }; + let [code] = this.check_shim(abi, exp_abi, link_name, args)?; + // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway + let code = this.read_scalar(code)?.to_i32()?; + throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); + } + "abort" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + throw_machine_stop!(TerminationInfo::Abort( + "the program aborted execution".to_owned() + )) + } + // Standard C allocation "malloc" => { let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -504,7 +476,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "__rust_alloc" => return this.emulate_allocator(default), "miri_alloc" => { default(this)?; - return Ok(EmulateForeignItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsJumping); } _ => unreachable!(), } @@ -564,7 +536,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "miri_dealloc" => { default(this)?; - return Ok(EmulateForeignItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsJumping); } _ => unreachable!(), } @@ -966,11 +938,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner( this, link_name, abi, args, dest, ), - _ => Ok(EmulateForeignItemResult::NotSupported), + _ => Ok(EmulateItemResult::NotSupported), }, }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/intrinsics/atomic.rs b/src/shims/intrinsics/atomic.rs index 4c5e2192e5..40f6b8a64e 100644 --- a/src/shims/intrinsics/atomic.rs +++ b/src/shims/intrinsics/atomic.rs @@ -20,7 +20,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { intrinsic_name: &str, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect(); @@ -114,9 +114,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; } - _ => return Ok(false), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(true) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/intrinsics/mod.rs b/src/shims/intrinsics/mod.rs index 2bf6980200..974943432a 100644 --- a/src/shims/intrinsics/mod.rs +++ b/src/shims/intrinsics/mod.rs @@ -10,8 +10,8 @@ use rustc_middle::{ mir, ty::{self, FloatTy}, }; -use rustc_target::abi::Size; use rustc_span::{sym, Symbol}; +use rustc_target::abi::Size; use crate::*; use atomic::EvalContextExt as _; @@ -37,51 +37,38 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let intrinsic_name = this.tcx.item_name(instance.def_id()); let intrinsic_name = intrinsic_name.as_str(); - // Handle intrinsics without return place. - match intrinsic_name { - "abort" => { - throw_machine_stop!(TerminationInfo::Abort( - "the program aborted execution".to_owned() - )) - } - _ => {} - } - - // All remaining supported intrinsics have a return place. - let ret = match ret { - // FIXME: add fallback body support once we actually have a diverging intrinsic with a fallback body - None => throw_unsup_format!("unimplemented (diverging) intrinsic: `{intrinsic_name}`"), - Some(p) => p, - }; - - // Some intrinsics are special and need the "ret". - match intrinsic_name { - "catch_unwind" => { - this.handle_catch_unwind(args, dest, ret)?; - return Ok(None); - } - _ => {} - } - - // The rest jumps to `ret` immediately. - if !this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest)? { - // We haven't handled the intrinsic, let's see if we can use a fallback body. - if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { - throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") + match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, dest, ret)? { + EmulateItemResult::NotSupported => { + // We haven't handled the intrinsic, let's see if we can use a fallback body. + if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { + throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") + } + let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub"); + if this + .tcx + .get_attrs_by_path( + instance.def_id(), + &[sym::miri, intrinsic_fallback_checks_ub], + ) + .next() + .is_none() + { + throw_unsup_format!( + "miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that" + ); + } + Ok(Some(ty::Instance { + def: ty::InstanceDef::Item(instance.def_id()), + args: instance.args, + })) } - let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub"); - if this.tcx.get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_checks_ub]).next().is_none() { - throw_unsup_format!("miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that"); + EmulateItemResult::NeedsJumping => { + trace!("{:?}", this.dump_place(&dest.clone().into())); + this.return_to_block(ret)?; + Ok(None) } - return Ok(Some(ty::Instance { - def: ty::InstanceDef::Item(instance.def_id()), - args: instance.args, - })) + EmulateItemResult::AlreadyJumped => Ok(None), } - - trace!("{:?}", this.dump_place(&dest.clone().into())); - this.go_to_block(ret); - Ok(None) } /// Emulates a Miri-supported intrinsic (not supported by the core engine). @@ -92,7 +79,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, bool> { + ret: Option, + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); if let Some(name) = intrinsic_name.strip_prefix("atomic_") { @@ -103,6 +91,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } match intrinsic_name { + "abort" => { + throw_machine_stop!(TerminationInfo::Abort( + "the program aborted execution".to_owned() + )); + } + "catch_unwind" => { + this.handle_catch_unwind(args, dest, ret)?; + // THis pushed a stack frame, don't jump to `ret`. + return Ok(EmulateItemResult::AlreadyJumped); + } + // Raw memory accesses "volatile_load" => { let [place] = check_arg_count(args)?; @@ -426,9 +425,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap"))) } - _ => return Ok(false), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(true) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/intrinsics/simd.rs b/src/shims/intrinsics/simd.rs index dca4c4ea5a..c7de587443 100644 --- a/src/shims/intrinsics/simd.rs +++ b/src/shims/intrinsics/simd.rs @@ -23,7 +23,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { generic_args: ty::GenericArgsRef<'tcx>, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, bool> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match intrinsic_name { #[rustfmt::skip] @@ -744,9 +744,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - _ => return Ok(false), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(true) + Ok(EmulateItemResult::NeedsJumping) } fn fminmax_op( diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 85c9a202f7..975efe29b4 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -16,3 +16,13 @@ pub mod os_str; pub mod panic; pub mod time; pub mod tls; + +/// What needs to be done after emulating an item (a shim or an intrinsic) is done. +pub enum EmulateItemResult { + /// The caller is expected to jump to the return block. + NeedsJumping, + /// Jumping has already been taken care of. + AlreadyJumped, + /// The item is not supported. + NotSupported, +} diff --git a/src/shims/panic.rs b/src/shims/panic.rs index bb31ef733c..4444d29746 100644 --- a/src/shims/panic.rs +++ b/src/shims/panic.rs @@ -30,7 +30,7 @@ pub struct CatchUnwindData<'tcx> { /// The return place from the original call to `try`. dest: MPlaceTy<'tcx, Provenance>, /// The return block from the original call to `try`. - ret: mir::BasicBlock, + ret: Option, } impl VisitProvenance for CatchUnwindData<'_> { @@ -73,7 +73,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ret: mir::BasicBlock, + ret: Option, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -103,7 +103,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &[data.into()], None, // Directly return to caller. - StackPopCleanup::Goto { ret: Some(ret), unwind: mir::UnwindAction::Continue }, + StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue }, )?; // We ourselves will return `0`, eventually (will be overwritten if we catch a panic). @@ -155,7 +155,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None, // Directly return to caller of `try`. StackPopCleanup::Goto { - ret: Some(catch_unwind.ret), + ret: catch_unwind.ret, unwind: mir::UnwindAction::Continue, }, )?; diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index bd299aaa12..7f6a3ba088 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -9,7 +9,6 @@ use rustc_target::spec::abi::Abi; use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; use shims::unix::freebsd::foreign_items as freebsd; use shims::unix::linux::foreign_items as linux; @@ -43,7 +42,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. @@ -750,11 +749,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), - _ => Ok(EmulateForeignItemResult::NotSupported), + _ => Ok(EmulateItemResult::NotSupported), }; } }; - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index ffb583123d..1e0e7d1632 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -3,7 +3,6 @@ use rustc_target::spec::abi::Abi; use crate::shims::unix::*; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; pub fn is_dyn_sym(_name: &str) -> bool { false @@ -17,7 +16,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match link_name.as_str() { // Threading @@ -97,8 +96,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 497e8bee70..dd4ec8cfaa 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -5,7 +5,6 @@ use crate::machine::SIGRTMAX; use crate::machine::SIGRTMIN; use crate::shims::unix::*; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; use shims::unix::linux::epoll::EvalContextExt as _; use shims::unix::linux::eventfd::EvalContextExt as _; use shims::unix::linux::mem::EvalContextExt as _; @@ -23,7 +22,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. @@ -156,7 +155,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } id => { this.handle_unsupported(format!("can't execute syscall with ID {id}"))?; - return Ok(EmulateForeignItemResult::AlreadyJumped); + return Ok(EmulateItemResult::AlreadyJumped); } } } @@ -204,10 +203,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), }; - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/unix/macos/foreign_items.rs b/src/shims/unix/macos/foreign_items.rs index 66a8dce753..912623b722 100644 --- a/src/shims/unix/macos/foreign_items.rs +++ b/src/shims/unix/macos/foreign_items.rs @@ -3,7 +3,6 @@ use rustc_target::spec::abi::Abi; use crate::shims::unix::*; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; pub fn is_dyn_sym(_name: &str) -> bool { false @@ -17,7 +16,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. @@ -175,9 +174,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(res, dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), }; - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index e8ae80261c..08aee04254 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -12,7 +12,6 @@ use crate::shims::alloc::EvalContextExt as _; use crate::shims::os_str::bytes_to_os_str; use crate::shims::windows::*; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; use shims::windows::handle::{Handle, PseudoHandle}; fn is_dyn_sym(name: &str) -> bool { @@ -86,7 +85,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern. @@ -721,9 +720,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/aesni.rs b/src/shims/x86/aesni.rs index 6c090d877e..8dc3748a12 100644 --- a/src/shims/x86/aesni.rs +++ b/src/shims/x86/aesni.rs @@ -4,7 +4,6 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -16,7 +15,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "aes")?; // Prefix should have already been checked. @@ -126,9 +125,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } // TODO: Implement the `llvm.x86.aesni.aeskeygenassist` when possible // with an external crate. - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/avx.rs b/src/shims/x86/avx.rs index 41c20d768f..86ef180a44 100644 --- a/src/shims/x86/avx.rs +++ b/src/shims/x86/avx.rs @@ -11,7 +11,6 @@ use super::{ FloatBinOp, FloatUnaryOp, }; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -23,7 +22,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "avx")?; // Prefix should have already been checked. @@ -343,8 +342,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_scalar(Scalar::from_i32(res.into()), dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/avx2.rs b/src/shims/x86/avx2.rs index bbf53f9f1e..6e3468c018 100644 --- a/src/shims/x86/avx2.rs +++ b/src/shims/x86/avx2.rs @@ -9,7 +9,6 @@ use super::{ packuswb, pmulhrsw, psign, shift_simd_by_scalar, shift_simd_by_simd, ShiftOp, }; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -21,7 +20,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "avx2")?; // Prefix should have already been checked. @@ -437,8 +436,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: shift_simd_by_simd(this, left, right, which, dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index cf4d6a04be..2ab2ae8590 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -10,7 +10,6 @@ use rustc_target::spec::abi::Abi; use crate::*; use helpers::bool_to_simd_element; -use shims::foreign_items::EmulateForeignItemResult; mod aesni; mod avx; @@ -31,7 +30,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap(); @@ -43,7 +42,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/addcarry-u32-addcarry-u64.html "addcarry.32" | "addcarry.64" => { if unprefixed_name == "addcarry.64" && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateForeignItemResult::NotSupported); + return Ok(EmulateItemResult::NotSupported); } let [c_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; @@ -69,7 +68,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/subborrow-u32-subborrow-u64.html "subborrow.32" | "subborrow.64" => { if unprefixed_name == "subborrow.64" && this.tcx.sess.target.arch != "x86_64" { - return Ok(EmulateForeignItemResult::NotSupported); + return Ok(EmulateItemResult::NotSupported); } let [b_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; @@ -143,9 +142,9 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: ); } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/sse.rs b/src/shims/x86/sse.rs index 1760883731..ea967a8cf7 100644 --- a/src/shims/x86/sse.rs +++ b/src/shims/x86/sse.rs @@ -8,7 +8,6 @@ use super::{ FloatUnaryOp, }; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -20,7 +19,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse")?; // Prefix should have already been checked. @@ -211,8 +210,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index c9ed751d36..31fa66a496 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -7,7 +7,6 @@ use super::{ packuswb, shift_simd_by_scalar, FloatBinOp, ShiftOp, }; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -19,7 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse2")?; // Prefix should have already been checked. @@ -387,8 +386,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/sse3.rs b/src/shims/x86/sse3.rs index 5ac30dca79..8de339eb9e 100644 --- a/src/shims/x86/sse3.rs +++ b/src/shims/x86/sse3.rs @@ -4,7 +4,6 @@ use rustc_target::spec::abi::Abi; use super::horizontal_bin_op; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -16,7 +15,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse3")?; // Prefix should have already been checked. @@ -50,8 +49,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.mem_copy(src_ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/sse41.rs b/src/shims/x86/sse41.rs index 19bc27421d..011a7a16c6 100644 --- a/src/shims/x86/sse41.rs +++ b/src/shims/x86/sse41.rs @@ -3,7 +3,6 @@ use rustc_target::spec::abi::Abi; use super::{conditional_dot_product, mpsadbw, packusdw, round_all, round_first, test_bits_masked}; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -15,7 +14,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?; // Prefix should have already been checked. @@ -175,8 +174,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_scalar(Scalar::from_i32(res.into()), dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } diff --git a/src/shims/x86/ssse3.rs b/src/shims/x86/ssse3.rs index 4f8e52dbb7..d30de43008 100644 --- a/src/shims/x86/ssse3.rs +++ b/src/shims/x86/ssse3.rs @@ -4,7 +4,6 @@ use rustc_target::spec::abi::Abi; use super::{horizontal_bin_op, int_abs, pmulhrsw, psign}; use crate::*; -use shims::foreign_items::EmulateForeignItemResult; impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: @@ -16,7 +15,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - ) -> InterpResult<'tcx, EmulateForeignItemResult> { + ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "ssse3")?; // Prefix should have already been checked. @@ -136,8 +135,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: psign(this, left, right, dest)?; } - _ => return Ok(EmulateForeignItemResult::NotSupported), + _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateForeignItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsJumping) } } From 8a0cfa1ff8d31e2b263aab45606e7f7949eebf51 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 17:52:50 +0200 Subject: [PATCH 154/208] sync: better error in invalid synchronization primitive ID --- src/concurrency/sync.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/concurrency/sync.rs b/src/concurrency/sync.rs index d3cef8bf5f..9467695599 100644 --- a/src/concurrency/sync.rs +++ b/src/concurrency/sync.rs @@ -305,6 +305,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let next_index = this.machine.threads.sync.mutexes.next_index(); if let Some(old) = existing(this, next_index)? { + if this.machine.threads.sync.mutexes.get(old).is_none() { + throw_ub_format!("mutex has invalid ID"); + } Ok(old) } else { let new_index = this.machine.threads.sync.mutexes.push(Default::default()); @@ -399,6 +402,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let next_index = this.machine.threads.sync.rwlocks.next_index(); if let Some(old) = existing(this, next_index)? { + if this.machine.threads.sync.rwlocks.get(old).is_none() { + throw_ub_format!("rwlock has invalid ID"); + } Ok(old) } else { let new_index = this.machine.threads.sync.rwlocks.push(Default::default()); @@ -563,6 +569,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let next_index = this.machine.threads.sync.condvars.next_index(); if let Some(old) = existing(this, next_index)? { + if this.machine.threads.sync.condvars.get(old).is_none() { + throw_ub_format!("condvar has invalid ID"); + } Ok(old) } else { let new_index = this.machine.threads.sync.condvars.push(Default::default()); From 45e2c5267391541ca1ad46941330410cac902bb3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 18:08:41 +0200 Subject: [PATCH 155/208] factor some pthread offset into constants --- src/shims/unix/sync.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index e50a8934e0..544f3259ee 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -65,7 +65,8 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( // (need to avoid this because it is set by static initializer macros) // bytes 4-7: mutex id as u32 or 0 if id is not assigned yet. // bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32 -// (the kind has to be at its offset for compatibility with static initializer macros) +// (the kind has to be at this particular offset for compatibility with Linux's static initializer +// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) fn mutex_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, @@ -123,11 +124,13 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( // (need to avoid this because it is set by static initializer macros) // bytes 4-7: rwlock id as u32 or 0 if id is not assigned yet. +const RWLOCK_ID_OFFSET: u64 = 4; + fn rwlock_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, rwlock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, RwLockId> { - ecx.rwlock_get_or_create_id(rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), 4) + ecx.rwlock_get_or_create_id(rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), RWLOCK_ID_OFFSET) } // pthread_condattr_t @@ -136,13 +139,15 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>( // store an i32 in the first four bytes equal to the corresponding libc clock id constant // (e.g. CLOCK_REALTIME). +const CONDATTR_CLOCK_OFFSET: u64 = 0; + fn condattr_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, attr_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( attr_op, - 0, + CONDATTR_CLOCK_OFFSET, ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, )? @@ -156,7 +161,7 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( attr_op, - 0, + CONDATTR_CLOCK_OFFSET, Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, @@ -172,11 +177,14 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( // bytes 4-7: the conditional variable id as u32 or 0 if id is not assigned yet. // bytes 8-11: the clock id constant as i32 +const CONDVAR_ID_OFFSET: u64 = 4; +const CONDVAR_CLOCK_OFFSET: u64 = 8; + fn cond_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, CondvarId> { - ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), 4) + ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), CONDVAR_ID_OFFSET) } fn cond_reset_id<'mir, 'tcx: 'mir>( @@ -185,7 +193,7 @@ fn cond_reset_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, - 4, + CONDVAR_ID_OFFSET, Scalar::from_i32(0), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.u32, @@ -198,7 +206,7 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( cond_op, - 8, + CONDVAR_CLOCK_OFFSET, ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, )? @@ -212,7 +220,7 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, - 8, + CONDVAR_CLOCK_OFFSET, Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, From 6b2e8f04593d23a2218f0f5599354b79d1b32c12 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 18:18:10 +0200 Subject: [PATCH 156/208] also test pthread_condattr_getclock --- src/shims/unix/sync.rs | 16 ++++++++++++++++ tests/pass-dep/shims/libc-rsfs.stdout | 1 - tests/pass-dep/shims/pthread-sync.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) delete mode 100644 tests/pass-dep/shims/libc-rsfs.stdout diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 544f3259ee..9c09676041 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -727,6 +727,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); + // Does not exist on macOS! + if !matches!(&*this.tcx.sess.target.os, "linux") { + throw_unsup_format!( + "`pthread_condattr_init` is not supported on {}", + this.tcx.sess.target.os + ); + } + let clock_id = this.read_scalar(clock_id_op)?.to_i32()?; if clock_id == this.eval_libc_i32("CLOCK_REALTIME") || clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") @@ -747,6 +755,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); + // Does not exist on macOS! + if !matches!(&*this.tcx.sess.target.os, "linux") { + throw_unsup_format!( + "`pthread_condattr_init` is not supported on {}", + this.tcx.sess.target.os + ); + } + let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?; diff --git a/tests/pass-dep/shims/libc-rsfs.stdout b/tests/pass-dep/shims/libc-rsfs.stdout deleted file mode 100644 index b6fa69e3d5..0000000000 --- a/tests/pass-dep/shims/libc-rsfs.stdout +++ /dev/null @@ -1 +0,0 @@ -hello dup fd diff --git a/tests/pass-dep/shims/pthread-sync.rs b/tests/pass-dep/shims/pthread-sync.rs index c9d10cb83d..12d3f2b6f1 100644 --- a/tests/pass-dep/shims/pthread-sync.rs +++ b/tests/pass-dep/shims/pthread-sync.rs @@ -19,6 +19,7 @@ fn main() { check_rwlock_write(); check_rwlock_read_no_deadlock(); check_cond(); + check_condattr(); } fn test_mutex_libc_init_recursive() { @@ -261,6 +262,31 @@ fn check_cond() { } } +fn check_condattr() { + unsafe { + // Just smoke-testing that these functions can be called. + let mut attr: MaybeUninit = MaybeUninit::uninit(); + assert_eq!(libc::pthread_condattr_init(attr.as_mut_ptr()), 0); + + #[cfg(not(target_os = "macos"))] // setclock-getclock do not exist on macOS + { + let clock_id = libc::CLOCK_MONOTONIC; + assert_eq!(libc::pthread_condattr_setclock(attr.as_mut_ptr(), clock_id), 0); + let mut check_clock_id = MaybeUninit::::uninit(); + assert_eq!( + libc::pthread_condattr_getclock(attr.as_mut_ptr(), check_clock_id.as_mut_ptr()), + 0 + ); + assert_eq!(check_clock_id.assume_init(), clock_id); + } + + let mut cond: MaybeUninit = MaybeUninit::uninit(); + assert_eq!(libc::pthread_cond_init(cond.as_mut_ptr(), attr.as_ptr()), 0); + assert_eq!(libc::pthread_condattr_destroy(attr.as_mut_ptr()), 0); + assert_eq!(libc::pthread_cond_destroy(cond.as_mut_ptr()), 0); + } +} + // std::sync::RwLock does not even used pthread_rwlock any more. // Do some smoke testing of the API surface. fn test_rwlock_libc_static_initializer() { From b17caf02f593593c710553d41b3b563eb4023e79 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 18:37:23 +0200 Subject: [PATCH 157/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index b2e6353778..038c2549b6 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -d6d3b342e85272f5e75c0d7a1dd3a1d8becb40ac +d7ea27808deb5e10a0f7384e339e4e6165e33398 From d2a1a4199eaf9b3de65d5564625b6b8dae4e0b90 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 18:39:45 +0200 Subject: [PATCH 158/208] bless and fmt --- src/intrinsics/mod.rs | 15 +++++++++++---- tests/fail/intrinsic_fallback_checks_ub.stderr | 3 ++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 8b078921fd..05fcaa69e7 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -10,8 +10,8 @@ use rustc_middle::{ mir, ty::{self, FloatTy}, }; -use rustc_target::abi::Size; use rustc_span::{sym, Symbol}; +use rustc_target::abi::Size; use crate::*; use atomic::EvalContextExt as _; @@ -70,13 +70,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`") } let intrinsic_fallback_checks_ub = Symbol::intern("intrinsic_fallback_checks_ub"); - if this.tcx.get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_checks_ub]).next().is_none() { - throw_unsup_format!("miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that"); + if this + .tcx + .get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_checks_ub]) + .next() + .is_none() + { + throw_unsup_format!( + "miri can only use intrinsic fallback bodies that check UB. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that" + ); } return Ok(Some(ty::Instance { def: ty::InstanceDef::Item(instance.def_id()), args: instance.args, - })) + })); } trace!("{:?}", this.dump_place(&dest.clone().into())); diff --git a/tests/fail/intrinsic_fallback_checks_ub.stderr b/tests/fail/intrinsic_fallback_checks_ub.stderr index b37e05c62f..5dd6520fe4 100644 --- a/tests/fail/intrinsic_fallback_checks_ub.stderr +++ b/tests/fail/intrinsic_fallback_checks_ub.stderr @@ -4,7 +4,8 @@ error: unsupported operation: miri can only use intrinsic fallback bodies that c LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC From 1aa686f96465b1375d64c703e9f00ff7082ae3ac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 19:09:40 +0200 Subject: [PATCH 159/208] remove some dead code --- bench-cargo-miri/mse/src/main.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/bench-cargo-miri/mse/src/main.rs b/bench-cargo-miri/mse/src/main.rs index c09dc2484d..e16ccd2c0d 100644 --- a/bench-cargo-miri/mse/src/main.rs +++ b/bench-cargo-miri/mse/src/main.rs @@ -4,9 +4,6 @@ static EXPECTED: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static PCM: &[i16] = &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, -2, 0, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 1, 0, 1, 0, 0, -2, 0, -2, 0, -2, 0, -2, -2, -2, -3, -3, -3, -3, -4, -2, -5, -2, -5, -2, -4, 0, -4, 0, -4, 0, -4, 1, -4, 1, -4, 2, -4, 2, -4, 2, -4, 2, -4, 2, -3, 1, -4, 0, -4, 0, -5, 0, -5, 0, -5, 0, -4, 2, -4, 3, -4, 4, -3, 5, -2, 5, -3, 6, -3, 6, -3, 5, -3, 5, -2, 4, -2, 3, -5, 0, -6, 0, -3, -2, -4, -4, -9, -5, -9, -4, -4, -2, -4, -2, -4, 0, -2, 1, 1, 1, 4, 2, 8, 2, 12, 1, 13, 0, 12, 0, 11, 0, 8, -2, 7, 0, 7, -3, 11, -8, 15, -9, 17, -6, 17, -5, 13, -3, 7, 0, 3, 0, -2, 0, -4, 0, -4, -2, -6, 0, -14, -2, -17, -4, -8, 0, -7, 5, -17, 7, -18, 10, -7, 18, -2, 25, -3, 27, 0, 31, 4, 34, 4, 34, 8, 36, 8, 37, 2, 36, 4, 34, 8, 28, 3, 15, 0, 11, 0, 12, -5, 8, -4, 10, 0, 23, -4, 31, -8, 30, -2, 30, 0, 26, -6, 22, -6, 20, -12, 15, -19, 10, -10, 13, -14, 6, -43, -13, -43, -16, -9, -12, -10, -29, -42, -40, -37, -28, -5, -21, 1, -24, -8, -20, 4, -18, 26, -24, 44, -26, 66, -30, 86, -37, 88, -41, 72, -46, 50, -31, 28, 23, 14, 64, 16, 51, 26, 32, 34, 39, 42, 48, 35, 58, 0, 72, -36, 69, -59, 58, -98, 54, -124, 36, -103, 12, -110, 5, -173, -19, -146, -59, -4, -42, 51, 1, -23, -6, -30, -6, 45, 46, 47, 70, 6, 55, 19, 60, 38, 62, 42, 47, 61, 46, 40, 42, -19, 22, -34, 6, -35, -50, -61, -141, -37, -171, 17, -163, 26, -180, 46, -154, 80, -63, 48, -4, 18, 20, 50, 47, 58, 53, 44, 61, 57, 85, 37, 80, 0, 86, -8, 106, -95, 49, -213, -8, -131, 47, 49, 63, 40, -39, -69, -74, -37, -20, 63, -12, 58, -14, -12, 25, -31, 41, 11, 45, 76, 47, 167, 5, 261, -37, 277, -83, 183, -172, 35, -122, -79, 138, -70, 266, 69, 124, 228, 0, 391, -29, 594, -84, 702, -78, 627, -8, 551, -13, 509, 13, 372, 120, 352, 125, 622, 127, 691, 223, 362, 126, 386, -33, 915, 198, 958, 457, 456, 298, 500, 233, 1027, 469, 1096, 426, 918, 160, 1067, 141, 1220, 189, 1245, 164, 1375, 297, 1378, 503, 1299, 702, 1550, 929, 1799, 855, 1752, 547, 1830, 602, 1928, 832, 1736, 796, 1735, 933, 1961, 1385, 1935, 1562, 2105, 1485, 2716, 1449, 2948, 1305, 2768, 1205, 2716, 1346, 2531, 1450, 2470, 1653, 3117, 2111, 3370, 2176, 2696, 1947, 2925, 2305, 3846, 2658, 2425, 2184, -877, 1981, -2261, 2623, -1645, 2908, -1876, 2732, -2704, 2953, -2484, 3116, -2120, 2954, -2442, 3216, -2466, 3499, -2192, 3234, -2392, 3361, -2497, 3869, -2078, 3772, -1858, 3915, -2066, 4438, -2285, 2934, -2294, -280, -2066, -1762, -1992, -1412, -2298, -1535, -2399, -1789, -2223, -1419, -2244, -1334, -2092, -1476, -1777, -1396, -2014, -1571, -2199, -1574, -1843, -1167, -1910, -1446, -2007, -1818]; fn main() { - #[cfg(increase_thread_usage)] - let thread = std::thread::spawn(|| 4); - for _ in 0..2 { mse(PCM.len(), PCM, EXPECTED); } From 100c718ee7bf479df7763f78b4c268a05a109333 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 18:56:02 +0200 Subject: [PATCH 160/208] only show the 'basic API common for this target' message when this is a missing foreign function --- src/diagnostics.rs | 13 ++++++++++--- src/helpers.rs | 10 ++++------ src/shims/foreign_items.rs | 4 ++-- src/shims/unix/linux/foreign_items.rs | 4 +++- tests/extern-so/fail/function_not_in_so.stderr | 4 ++-- tests/fail-dep/shims/fs/close_stdout.stderr | 1 - tests/fail-dep/shims/fs/read_from_stdout.stderr | 1 - tests/fail-dep/shims/fs/write_to_stdin.stderr | 1 - tests/fail-dep/tokio/sleep.stderr | 1 - .../fail-dep/unsupported_incomplete_function.stderr | 4 ++-- tests/fail/alloc/no_global_allocator.stderr | 4 ++-- tests/fail/extern-type-field-offset.stderr | 1 - tests/fail/extern_static.stderr | 1 - tests/fail/extern_static_in_const.stderr | 1 - tests/fail/extern_static_wrong_size.stderr | 1 - .../abi_mismatch_array_vs_struct.stderr | 2 +- .../abi_mismatch_int_vs_float.stderr | 2 +- .../abi_mismatch_raw_pointer.stderr | 2 +- .../function_pointers/abi_mismatch_repr_C.stderr | 2 +- .../abi_mismatch_return_type.stderr | 2 +- .../function_pointers/abi_mismatch_simple.stderr | 2 +- .../function_pointers/abi_mismatch_vector.stderr | 2 +- tests/fail/intrinsic_fallback_checks_ub.stderr | 1 - ...3288-ice-symbolic-alignment-extern-static.stderr | 1 - .../fail/shims/backtrace/bad-backtrace-flags.stderr | 1 - .../backtrace/bad-backtrace-resolve-flags.stderr | 1 - .../bad-backtrace-resolve-names-flags.stderr | 1 - .../shims/backtrace/bad-backtrace-size-flags.stderr | 1 - .../promise_alignment.call_unaligned_ptr.stderr | 1 - .../promise_alignment_zero.stderr | 1 - tests/fail/unsized-local.stderr | 1 - tests/fail/unsupported_foreign_function.stderr | 4 ++-- 32 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/diagnostics.rs b/src/diagnostics.rs index 5fac922f56..cb5ed27b6c 100644 --- a/src/diagnostics.rs +++ b/src/diagnostics.rs @@ -48,6 +48,7 @@ pub enum TerminationInfo { extra: Option<&'static str>, retag_explain: bool, }, + UnsupportedForeignItem(String), } pub struct RacingOp { @@ -85,6 +86,7 @@ impl fmt::Display for TerminationInfo { op2.action, op2.thread_info ), + UnsupportedForeignItem(msg) => write!(f, "{msg}"), } } } @@ -214,7 +216,7 @@ pub fn report_error<'tcx, 'mir>( let title = match info { Exit { code, leak_check } => return Some((*code, *leak_check)), Abort(_) => Some("abnormal termination"), - UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance => + UnsupportedInIsolation(_) | Int2PtrWithStrictProvenance | UnsupportedForeignItem(_) => Some("unsupported operation"), StackedBorrowsUb { .. } | TreeBorrowsUb { .. } | DataRace { .. } => Some("Undefined Behavior"), @@ -228,6 +230,12 @@ pub fn report_error<'tcx, 'mir>( (None, format!("pass the flag `-Zmiri-disable-isolation` to disable isolation;")), (None, format!("or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning")), ], + UnsupportedForeignItem(_) => { + vec![ + (None, format!("if this is a basic API commonly used on this target, please report an issue with Miri")), + (None, format!("however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases")), + ] + } StackedBorrowsUb { help, history, .. } => { msg.extend(help.clone()); let mut helps = vec![ @@ -322,7 +330,6 @@ pub fn report_error<'tcx, 'mir>( Unsupported(_) => vec![ (None, format!("this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support")), - (None, format!("if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there")), ], UndefinedBehavior(AlignmentCheckFailed { .. }) if ecx.machine.check_alignment == AlignmentCheck::Symbolic @@ -347,7 +354,7 @@ pub fn report_error<'tcx, 'mir>( } AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => { helps.push((None, format!("this means these two types are not *guaranteed* to be ABI-compatible across all targets"))); - helps.push((None, format!("if you think this code should be accepted anyway, please report an issue"))); + helps.push((None, format!("if you think this code should be accepted anyway, please report an issue with Miri"))); } _ => {}, } diff --git a/src/helpers.rs b/src/helpers.rs index 92bdaf3017..f81e997efd 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1067,20 +1067,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { crate_name == "std" || crate_name == "std_miri_test" } - /// Handler that should be called when unsupported functionality is encountered. + /// Handler that should be called when an unsupported foreign item is encountered. /// This function will either panic within the context of the emulated application /// or return an error in the Miri process context - /// - /// Return value of `Ok(bool)` indicates whether execution should continue. - fn handle_unsupported>(&mut self, error_msg: S) -> InterpResult<'tcx, ()> { + fn handle_unsupported_foreign_item(&mut self, error_msg: String) -> InterpResult<'tcx, ()> { let this = self.eval_context_mut(); if this.machine.panic_on_unsupported { // message is slightly different here to make automated analysis easier - let error_msg = format!("unsupported Miri functionality: {}", error_msg.as_ref()); + let error_msg = format!("unsupported Miri functionality: {error_msg}"); this.start_panic(error_msg.as_ref(), mir::UnwindAction::Continue)?; Ok(()) } else { - throw_unsup_format!("{}", error_msg.as_ref()); + throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(error_msg)); } } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 4f8bb80110..7c97d597ae 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -119,7 +119,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let Some(body) = this.lookup_exported_symbol(link_name)? { return Ok(Some(body)); } - this.handle_unsupported(format!( + this.handle_unsupported_foreign_item(format!( "can't call (diverging) foreign function: {link_name}" ))?; return Ok(None); @@ -140,7 +140,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(Some(body)); } - this.handle_unsupported(format!( + this.handle_unsupported_foreign_item(format!( "can't call foreign function `{link_name}` on OS `{os}`", os = this.tcx.sess.target.os, ))?; diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index a72ca510b4..9d1deb3201 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -144,7 +144,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { futex(this, &args[1..], dest)?; } id => { - this.handle_unsupported(format!("can't execute syscall with ID {id}"))?; + this.handle_unsupported_foreign_item(format!( + "can't execute syscall with ID {id}" + ))?; return Ok(EmulateForeignItemResult::AlreadyJumped); } } diff --git a/tests/extern-so/fail/function_not_in_so.stderr b/tests/extern-so/fail/function_not_in_so.stderr index 26761c4872..e905d7d039 100644 --- a/tests/extern-so/fail/function_not_in_so.stderr +++ b/tests/extern-so/fail/function_not_in_so.stderr @@ -4,8 +4,8 @@ error: unsupported operation: can't call foreign function `foo` on $OS LL | foo(); | ^^^^^ can't call foreign function `foo` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there + = help: if this is a basic API commonly used on this target, please report an issue with Miri + = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases = note: BACKTRACE: = note: inside `main` at $DIR/function_not_in_so.rs:LL:CC diff --git a/tests/fail-dep/shims/fs/close_stdout.stderr b/tests/fail-dep/shims/fs/close_stdout.stderr index e504801576..e1b1b053bb 100644 --- a/tests/fail-dep/shims/fs/close_stdout.stderr +++ b/tests/fail-dep/shims/fs/close_stdout.stderr @@ -5,7 +5,6 @@ LL | libc::close(1); | ^^^^^^^^^^^^^^ cannot close stdout | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/close_stdout.rs:LL:CC diff --git a/tests/fail-dep/shims/fs/read_from_stdout.stderr b/tests/fail-dep/shims/fs/read_from_stdout.stderr index fb0fb8acc3..baa6eb5ad6 100644 --- a/tests/fail-dep/shims/fs/read_from_stdout.stderr +++ b/tests/fail-dep/shims/fs/read_from_stdout.stderr @@ -5,7 +5,6 @@ LL | libc::read(1, bytes.as_mut_ptr() as *mut libc::c_void, 512); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot read from stdout | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/read_from_stdout.rs:LL:CC diff --git a/tests/fail-dep/shims/fs/write_to_stdin.stderr b/tests/fail-dep/shims/fs/write_to_stdin.stderr index 8426c5cb84..37323faf56 100644 --- a/tests/fail-dep/shims/fs/write_to_stdin.stderr +++ b/tests/fail-dep/shims/fs/write_to_stdin.stderr @@ -5,7 +5,6 @@ LL | libc::write(0, bytes.as_ptr() as *const libc::c_void, 5); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot write to stdin | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/write_to_stdin.rs:LL:CC diff --git a/tests/fail-dep/tokio/sleep.stderr b/tests/fail-dep/tokio/sleep.stderr index f7d829f77d..4b12729bbf 100644 --- a/tests/fail-dep/tokio/sleep.stderr +++ b/tests/fail-dep/tokio/sleep.stderr @@ -10,7 +10,6 @@ LL | | )) | |__________^ returning ready events from epoll_wait is not yet implemented | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there error: aborting due to 1 previous error diff --git a/tests/fail-dep/unsupported_incomplete_function.stderr b/tests/fail-dep/unsupported_incomplete_function.stderr index 23327ff7db..f62622e29b 100644 --- a/tests/fail-dep/unsupported_incomplete_function.stderr +++ b/tests/fail-dep/unsupported_incomplete_function.stderr @@ -4,8 +4,8 @@ error: unsupported operation: can't call foreign function `signal` on $OS LL | libc::signal(libc::SIGPIPE, libc::SIG_IGN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function `signal` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there + = help: if this is a basic API commonly used on this target, please report an issue with Miri + = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases = note: BACKTRACE: = note: inside `main` at $DIR/unsupported_incomplete_function.rs:LL:CC diff --git a/tests/fail/alloc/no_global_allocator.stderr b/tests/fail/alloc/no_global_allocator.stderr index 70f60a3f59..82bcb48cbe 100644 --- a/tests/fail/alloc/no_global_allocator.stderr +++ b/tests/fail/alloc/no_global_allocator.stderr @@ -4,8 +4,8 @@ error: unsupported operation: can't call foreign function `__rust_alloc` on $OS LL | __rust_alloc(1, 1); | ^^^^^^^^^^^^^^^^^^ can't call foreign function `__rust_alloc` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there + = help: if this is a basic API commonly used on this target, please report an issue with Miri + = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases = note: BACKTRACE: = note: inside `start` at $DIR/no_global_allocator.rs:LL:CC diff --git a/tests/fail/extern-type-field-offset.stderr b/tests/fail/extern-type-field-offset.stderr index 14609ec699..e0d6e9ebf1 100644 --- a/tests/fail/extern-type-field-offset.stderr +++ b/tests/fail/extern-type-field-offset.stderr @@ -5,7 +5,6 @@ LL | let _field = &x.a; | ^^^^ `extern type` does not have a known offset | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern-type-field-offset.rs:LL:CC diff --git a/tests/fail/extern_static.stderr b/tests/fail/extern_static.stderr index 1d12fd76be..21759f9601 100644 --- a/tests/fail/extern_static.stderr +++ b/tests/fail/extern_static.stderr @@ -5,7 +5,6 @@ LL | let _val = unsafe { std::ptr::addr_of!(FOO) }; | ^^^ extern static `FOO` is not supported by Miri | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern_static.rs:LL:CC diff --git a/tests/fail/extern_static_in_const.stderr b/tests/fail/extern_static_in_const.stderr index 455d6af18e..aa524c0646 100644 --- a/tests/fail/extern_static_in_const.stderr +++ b/tests/fail/extern_static_in_const.stderr @@ -5,7 +5,6 @@ LL | let _val = X; | ^ extern static `E` is not supported by Miri | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern_static_in_const.rs:LL:CC diff --git a/tests/fail/extern_static_wrong_size.stderr b/tests/fail/extern_static_wrong_size.stderr index 91afb936fa..3c013a5d15 100644 --- a/tests/fail/extern_static_wrong_size.stderr +++ b/tests/fail/extern_static_wrong_size.stderr @@ -5,7 +5,6 @@ LL | let _val = unsafe { environ }; | ^^^^^^^ extern static `environ` has been declared as `extern_static_wrong_size::environ` with a size of 1 bytes and alignment of 1 bytes, but Miri emulates it via an extern static shim with a size of N bytes and alignment of N bytes | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/extern_static_wrong_size.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr b/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr index 595235088f..2b2a898ce7 100644 --- a/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr +++ b/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr @@ -7,7 +7,7 @@ LL | g(Default::default()) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_array_vs_struct.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr b/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr index a8b1cf40c0..752e17116d 100644 --- a/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr +++ b/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr @@ -7,7 +7,7 @@ LL | g(42) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_int_vs_float.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr b/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr index 60dd3814bf..907a8e50c4 100644 --- a/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr +++ b/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr @@ -7,7 +7,7 @@ LL | g(&42 as *const i32) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_raw_pointer.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_repr_C.stderr b/tests/fail/function_pointers/abi_mismatch_repr_C.stderr index 6d42bea9da..eaacc32bf6 100644 --- a/tests/fail/function_pointers/abi_mismatch_repr_C.stderr +++ b/tests/fail/function_pointers/abi_mismatch_repr_C.stderr @@ -7,7 +7,7 @@ LL | fnptr(S1(NonZeroI32::new(1).unwrap())); = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_repr_C.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_return_type.stderr b/tests/fail/function_pointers/abi_mismatch_return_type.stderr index 198896b0dd..3793590f84 100644 --- a/tests/fail/function_pointers/abi_mismatch_return_type.stderr +++ b/tests/fail/function_pointers/abi_mismatch_return_type.stderr @@ -7,7 +7,7 @@ LL | g() = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_return_type.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_simple.stderr b/tests/fail/function_pointers/abi_mismatch_simple.stderr index daf216a142..0c533c1417 100644 --- a/tests/fail/function_pointers/abi_mismatch_simple.stderr +++ b/tests/fail/function_pointers/abi_mismatch_simple.stderr @@ -7,7 +7,7 @@ LL | g(42) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_simple.rs:LL:CC diff --git a/tests/fail/function_pointers/abi_mismatch_vector.stderr b/tests/fail/function_pointers/abi_mismatch_vector.stderr index 50f4474cc0..ef4b60b83b 100644 --- a/tests/fail/function_pointers/abi_mismatch_vector.stderr +++ b/tests/fail/function_pointers/abi_mismatch_vector.stderr @@ -7,7 +7,7 @@ LL | g(Default::default()) = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at $DIR/abi_mismatch_vector.rs:LL:CC diff --git a/tests/fail/intrinsic_fallback_checks_ub.stderr b/tests/fail/intrinsic_fallback_checks_ub.stderr index 5dd6520fe4..699dda5209 100644 --- a/tests/fail/intrinsic_fallback_checks_ub.stderr +++ b/tests/fail/intrinsic_fallback_checks_ub.stderr @@ -5,7 +5,6 @@ LL | ptr_guaranteed_cmp::<()>(std::ptr::null(), std::ptr::null()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ miri can only use intrinsic fallback bodies that check UB. After verifying that `ptr_guaranteed_cmp` does so, add the `#[miri::intrinsic_fallback_checks_ub]` attribute to it; also ping @rust-lang/miri when you do that | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/intrinsic_fallback_checks_ub.rs:LL:CC diff --git a/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr b/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr index 0d10b03fc4..4064d7fe4e 100644 --- a/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr +++ b/tests/fail/issue-miri-3288-ice-symbolic-alignment-extern-static.stderr @@ -5,7 +5,6 @@ LL | let _val = *DISPATCH_QUEUE_CONCURRENT; | ^^^^^^^^^^^^^^^^^^^^^^^^^ extern static `_dispatch_queue_attr_concurrent` is not supported by Miri | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/issue-miri-3288-ice-symbolic-alignment-extern-static.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-flags.stderr index ced44ee776..504485e3b3 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-flags.stderr @@ -5,7 +5,6 @@ LL | miri_get_backtrace(2, std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_get_backtrace` flags 2 | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-flags.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr index 6e282ff490..c1f0ce3d1a 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-resolve-flags.stderr @@ -5,7 +5,6 @@ LL | miri_resolve_frame(buf[0], 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_resolve_frame` flags 2 | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-resolve-flags.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr index ddf8d221cc..fc270593e6 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-resolve-names-flags.stderr @@ -5,7 +5,6 @@ LL | ... miri_resolve_frame_names(buf[0], 2, std::ptr::null_mut(), std::ptr::n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_resolve_frame_names` flags 2 | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-resolve-names-flags.rs:LL:CC diff --git a/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr b/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr index b3186fc5b0..2d70733334 100644 --- a/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr +++ b/tests/fail/shims/backtrace/bad-backtrace-size-flags.stderr @@ -5,7 +5,6 @@ LL | miri_backtrace_size(2); | ^^^^^^^^^^^^^^^^^^^^^^ unknown `miri_backtrace_size` flags 2 | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/bad-backtrace-size-flags.rs:LL:CC diff --git a/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr b/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr index 9ad715f7ba..6d62db4d3d 100644 --- a/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr +++ b/tests/fail/unaligned_pointers/promise_alignment.call_unaligned_ptr.stderr @@ -5,7 +5,6 @@ LL | unsafe { utils::miri_promise_symbolic_alignment(align8.add(1).cast( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: pointer is not actually aligned | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/promise_alignment.rs:LL:CC diff --git a/tests/fail/unaligned_pointers/promise_alignment_zero.stderr b/tests/fail/unaligned_pointers/promise_alignment_zero.stderr index 62b6e85231..3f7ced0b40 100644 --- a/tests/fail/unaligned_pointers/promise_alignment_zero.stderr +++ b/tests/fail/unaligned_pointers/promise_alignment_zero.stderr @@ -5,7 +5,6 @@ LL | unsafe { utils::miri_promise_symbolic_alignment(buffer.as_ptr().cast(), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: alignment must be a power of 2, got 0 | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/promise_alignment_zero.rs:LL:CC diff --git a/tests/fail/unsized-local.stderr b/tests/fail/unsized-local.stderr index d938f36bd5..df54c98bb0 100644 --- a/tests/fail/unsized-local.stderr +++ b/tests/fail/unsized-local.stderr @@ -5,7 +5,6 @@ LL | let x = *(Box::new(A) as Box); | ^ unsized locals are not supported | = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there = note: BACKTRACE: = note: inside `main` at $DIR/unsized-local.rs:LL:CC diff --git a/tests/fail/unsupported_foreign_function.stderr b/tests/fail/unsupported_foreign_function.stderr index 7492fdd77b..f392e9564c 100644 --- a/tests/fail/unsupported_foreign_function.stderr +++ b/tests/fail/unsupported_foreign_function.stderr @@ -4,8 +4,8 @@ error: unsupported operation: can't call foreign function `foo` on $OS LL | foo(); | ^^^^^ can't call foreign function `foo` on $OS | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - = help: if this is a basic API commonly used on this target, please report an issue; but note that Miri does not aim to support every FFI function out there + = help: if this is a basic API commonly used on this target, please report an issue with Miri + = help: however, note that Miri does not aim to support every FFI function out there; for instance, we will not support APIs for things such as GUIs, scripting languages, or databases = note: BACKTRACE: = note: inside `main` at $DIR/unsupported_foreign_function.rs:LL:CC From e400f8c1f4e9f731889ea95009337607e8b7ea42 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 22:28:57 +0200 Subject: [PATCH 161/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 038c2549b6..ca6f4d5091 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -d7ea27808deb5e10a0f7384e339e4e6165e33398 +d568423a7a4ddb4b49323d96078a22f94df55fbd From 3f3ae67b87da9e18e785ff7462035820dc4f081b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 22:44:56 +0200 Subject: [PATCH 162/208] rustc_pull: explain order of operations --- miri-script/src/commands.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 7e34ad050b..6d07455c0d 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -239,6 +239,8 @@ impl Command { // the merge has confused the heck out of josh in the past. // We pass `--no-verify` to avoid running git hooks like `./miri fmt` that could in turn // trigger auto-actions. + // We do this before the merge so that if there are merge conflicts, we have + // the right rust-version file while resolving them. sh.write_file("rust-version", format!("{commit}\n"))?; const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") From 7e98cb788c2bcd06d792df9ce8747f8f86238624 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 22:48:02 +0200 Subject: [PATCH 163/208] fix/extend some comments --- src/intrinsics/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 82b6c3d106..effd7f6d54 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -91,6 +91,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } match intrinsic_name { + // Basic control flow "abort" => { throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() @@ -98,7 +99,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "catch_unwind" => { this.handle_catch_unwind(args, dest, ret)?; - // THis pushed a stack frame, don't jump to `ret`. + // This pushed a stack frame, don't jump to `ret`. return Ok(EmulateItemResult::AlreadyJumped); } From c3ee0077a3e43b01eaa02985b4946b74be9b8bc0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 4 May 2024 22:50:46 +0200 Subject: [PATCH 164/208] make ExitProcess Windows-only --- src/shims/foreign_items.rs | 10 ++-------- src/shims/windows/foreign_items.rs | 6 ++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 954405de86..c51a27b745 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -405,14 +405,8 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Aborting the process. - "exit" | "ExitProcess" => { - let exp_abi = if link_name.as_str() == "exit" { - Abi::C { unwind: false } - } else { - Abi::System { unwind: false } - }; - let [code] = this.check_shim(abi, exp_abi, link_name, args)?; - // it's really u32 for ExitProcess, but we have to put it into the `Exit` variant anyway + "exit" => { + let [code] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 92676e1b99..dba5b7a906 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -506,6 +506,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Miscellaneous + "ExitProcess" => { + let [code] = + this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; + let code = this.read_scalar(code)?.to_u32()?; + throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); + } "SystemFunction036" => { // This is really 'RtlGenRandom'. let [ptr, len] = From de5464233a0e430943a9e886817efefe81f72e90 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 1 Dec 2023 23:41:07 +0000 Subject: [PATCH 165/208] illumos support. --- README.md | 1 + ci/ci.sh | 3 ++ src/concurrency/thread.rs | 7 +++ src/shims/unix/foreign_items.rs | 8 ++- src/shims/unix/mod.rs | 1 + src/shims/unix/solarish/foreign_items.rs | 41 ++++++++++++++++ src/shims/unix/solarish/mod.rs | 1 + src/shims/unix/sync.rs | 62 ++++++++++++++++-------- src/shims/unix/thread.rs | 2 +- 9 files changed, 102 insertions(+), 24 deletions(-) create mode 100644 src/shims/unix/solarish/foreign_items.rs create mode 100644 src/shims/unix/solarish/mod.rs diff --git a/README.md b/README.md index f92cc088e0..5b3e2a588b 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,7 @@ degree documented below): - We have unofficial support (not maintained by the Miri team itself) for some further operating systems. - `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. + - `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works. - `wasm`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. diff --git a/ci/ci.sh b/ci/ci.sh index d3dee0de61..8b7d23621a 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -146,6 +146,9 @@ case $HOST_TARGET in MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic + MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync + # TODO fix solaris stack guard + # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index 0116bd0281..6953ce81c5 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -78,6 +78,13 @@ impl TryFrom for ThreadId { } } +impl TryFrom for ThreadId { + type Error = TryFromIntError; + fn try_from(id: i128) -> Result { + u32::try_from(id).map(Self) + } +} + impl From for ThreadId { fn from(id: u32) -> Self { Self(id) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 90e0406f09..3d3a510c4a 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -13,6 +13,7 @@ use crate::*; use shims::unix::freebsd::foreign_items as freebsd; use shims::unix::linux::foreign_items as linux; use shims::unix::macos::foreign_items as macos; +use shims::unix::solarish::foreign_items as solarish; pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { match name { @@ -29,6 +30,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { "freebsd" => freebsd::is_dyn_sym(name), "linux" => linux::is_dyn_sym(name), "macos" => macos::is_dyn_sym(name), + "solaris" | "illumos" => solarish::is_dyn_sym(name), _ => false, }, } @@ -591,8 +593,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "getentropy" => { // This function is non-standard but exists with the same signature and behavior on - // Linux, macOS, and FreeBSD. - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd") { + // Linux, macOS, FreeBSD and Solaris/Illumos. + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris") { throw_unsup_format!( "`getentropy` is not supported on {}", this.tcx.sess.target.os @@ -608,6 +610,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=getentropy&sektion=3&format=html // Linux: https://man7.org/linux/man-pages/man3/getentropy.3.html // macOS: https://keith.github.io/xcode-man-pages/getentropy.2.html + // Solaris/Illumos: https://illumos.org/man/3C/getentropy if bufsize > 256 { let err = this.eval_libc("EIO"); this.set_last_error(err)?; @@ -730,6 +733,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), + "solaris" | "illumos" => solarish::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest), _ => Ok(EmulateItemResult::NotSupported), }; } diff --git a/src/shims/unix/mod.rs b/src/shims/unix/mod.rs index bede2fbd38..6dee30d895 100644 --- a/src/shims/unix/mod.rs +++ b/src/shims/unix/mod.rs @@ -11,6 +11,7 @@ mod thread; mod freebsd; mod linux; mod macos; +mod solarish; pub use env::UnixEnvVars; pub use fd::{FdTable, FileDescription}; diff --git a/src/shims/unix/solarish/foreign_items.rs b/src/shims/unix/solarish/foreign_items.rs new file mode 100644 index 0000000000..5bf4a7621d --- /dev/null +++ b/src/shims/unix/solarish/foreign_items.rs @@ -0,0 +1,41 @@ +use rustc_span::Symbol; +use rustc_target::spec::abi::Abi; + +use crate::shims::unix::*; +use crate::*; +use shims::EmulateItemResult; + +pub fn is_dyn_sym(_name: &str) -> bool { + false +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + fn emulate_foreign_item_inner( + &mut self, + link_name: Symbol, + abi: Abi, + args: &[OpTy<'tcx, Provenance>], + dest: &MPlaceTy<'tcx, Provenance>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + match link_name.as_str() { + // Threading + "pthread_condattr_setclock" => { + let [attr, clock_id] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.pthread_condattr_setclock(attr, clock_id)?; + this.write_scalar(result, dest)?; + } + "pthread_condattr_getclock" => { + let [attr, clock_id] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.pthread_condattr_getclock(attr, clock_id)?; + this.write_scalar(result, dest)?; + } + + _ => return Ok(EmulateItemResult::NotSupported), + } + Ok(EmulateItemResult::NeedsJumping) + } +} diff --git a/src/shims/unix/solarish/mod.rs b/src/shims/unix/solarish/mod.rs new file mode 100644 index 0000000000..09c6507b24 --- /dev/null +++ b/src/shims/unix/solarish/mod.rs @@ -0,0 +1 @@ +pub mod foreign_items; diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 9c09676041..2c9208deb3 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -68,11 +68,20 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( // (the kind has to be at this particular offset for compatibility with Linux's static initializer // macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) +#[inline] +fn mutex_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { + if matches!(&*ecx.tcx.sess.target.os, "macos") { 4 } else { 0 } +} + fn mutex_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, MutexId> { - ecx.mutex_get_or_create_id(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), 4) + ecx.mutex_get_or_create_id( + mutex_op, + ecx.libc_ty_layout("pthread_mutex_t"), + mutex_id_offset(ecx), + ) } fn mutex_reset_id<'mir, 'tcx: 'mir>( @@ -81,7 +90,7 @@ fn mutex_reset_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( mutex_op, - 4, + mutex_id_offset(ecx), Scalar::from_i32(0), ecx.libc_ty_layout("pthread_mutex_t"), ecx.machine.layouts.u32, @@ -124,13 +133,20 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( // (need to avoid this because it is set by static initializer macros) // bytes 4-7: rwlock id as u32 or 0 if id is not assigned yet. -const RWLOCK_ID_OFFSET: u64 = 4; +#[inline] +fn rwlock_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { + if matches!(&*ecx.tcx.sess.target.os, "macos") { 4 } else { 0 } +} fn rwlock_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, rwlock_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, RwLockId> { - ecx.rwlock_get_or_create_id(rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), RWLOCK_ID_OFFSET) + ecx.rwlock_get_or_create_id( + rwlock_op, + ecx.libc_ty_layout("pthread_rwlock_t"), + rwlock_id_offset(ecx), + ) } // pthread_condattr_t @@ -177,14 +193,18 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( // bytes 4-7: the conditional variable id as u32 or 0 if id is not assigned yet. // bytes 8-11: the clock id constant as i32 -const CONDVAR_ID_OFFSET: u64 = 4; const CONDVAR_CLOCK_OFFSET: u64 = 8; +#[inline] +fn cond_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { + if matches!(&*ecx.tcx.sess.target.os, "macos") { 4 } else { 0 } +} + fn cond_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, CondvarId> { - ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), CONDVAR_ID_OFFSET) + ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), cond_id_offset(ecx)) } fn cond_reset_id<'mir, 'tcx: 'mir>( @@ -193,7 +213,7 @@ fn cond_reset_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, - CONDVAR_ID_OFFSET, + cond_id_offset(ecx), Scalar::from_i32(0), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.u32, @@ -287,7 +307,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_mutexattr_init` is not supported on {}", this.tcx.sess.target.os @@ -376,7 +396,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_mutex_init` is not supported on {}", this.tcx.sess.target.os @@ -537,7 +557,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_rwlock_rdlock` is not supported on {}", this.tcx.sess.target.os @@ -562,7 +582,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_rwlock_tryrdlock` is not supported on {}", this.tcx.sess.target.os @@ -586,7 +606,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_rwlock_wrlock` is not supported on {}", this.tcx.sess.target.os @@ -623,7 +643,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_rwlock_trywrlock` is not supported on {}", this.tcx.sess.target.os @@ -647,7 +667,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_rwlock_unlock` is not supported on {}", this.tcx.sess.target.os @@ -673,7 +693,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_rwlock_destroy` is not supported on {}", this.tcx.sess.target.os @@ -704,7 +724,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_condattr_init` is not supported on {}", this.tcx.sess.target.os @@ -728,9 +748,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); // Does not exist on macOS! - if !matches!(&*this.tcx.sess.target.os, "linux") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") { throw_unsup_format!( - "`pthread_condattr_init` is not supported on {}", + "`pthread_condattr_setclock` is not supported on {}", this.tcx.sess.target.os ); } @@ -756,9 +776,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); // Does not exist on macOS! - if !matches!(&*this.tcx.sess.target.os, "linux") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") { throw_unsup_format!( - "`pthread_condattr_init` is not supported on {}", + "`pthread_condattr_getclock` is not supported on {}", this.tcx.sess.target.os ); } @@ -793,7 +813,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { throw_unsup_format!( "`pthread_cond_init` is not supported on {}", this.tcx.sess.target.os diff --git a/src/shims/unix/thread.rs b/src/shims/unix/thread.rs index 2a56cd35dc..7f814e080f 100644 --- a/src/shims/unix/thread.rs +++ b/src/shims/unix/thread.rs @@ -42,7 +42,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_unsup_format!("Miri supports pthread_join only with retval==NULL"); } - let thread_id = this.read_target_usize(thread)?; + let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?; Ok(0) From c1d902fcf84126370a92c87e917897c2b7c5a243 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 10:28:38 +0200 Subject: [PATCH 166/208] pthread shims: reorganize field offset handling --- src/shims/unix/foreign_items.rs | 12 + src/shims/unix/linux/foreign_items.rs | 12 - src/shims/unix/solarish/foreign_items.rs | 16 +- src/shims/unix/sync.rs | 284 ++++++++---------- .../libc_pthread_condattr_double_destroy.rs | 1 + 5 files changed, 134 insertions(+), 191 deletions(-) diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 3d3a510c4a..45793602b2 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -490,6 +490,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let result = this.pthread_condattr_init(attr)?; this.write_scalar(Scalar::from_i32(result), dest)?; } + "pthread_condattr_setclock" => { + let [attr, clock_id] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.pthread_condattr_setclock(attr, clock_id)?; + this.write_scalar(result, dest)?; + } + "pthread_condattr_getclock" => { + let [attr, clock_id] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let result = this.pthread_condattr_getclock(attr, clock_id)?; + this.write_scalar(result, dest)?; + } "pthread_condattr_destroy" => { let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let result = this.pthread_condattr_destroy(attr)?; diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 9b3302359a..43b5db9e32 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -73,18 +73,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Threading - "pthread_condattr_setclock" => { - let [attr, clock_id] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_setclock(attr, clock_id)?; - this.write_scalar(result, dest)?; - } - "pthread_condattr_getclock" => { - let [attr, clock_id] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_getclock(attr, clock_id)?; - this.write_scalar(result, dest)?; - } "pthread_setname_np" => { let [thread, name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/src/shims/unix/solarish/foreign_items.rs b/src/shims/unix/solarish/foreign_items.rs index 5bf4a7621d..16a0472756 100644 --- a/src/shims/unix/solarish/foreign_items.rs +++ b/src/shims/unix/solarish/foreign_items.rs @@ -1,7 +1,6 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; -use crate::shims::unix::*; use crate::*; use shims::EmulateItemResult; @@ -11,6 +10,7 @@ pub fn is_dyn_sym(_name: &str) -> bool { impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + #[allow(warnings)] fn emulate_foreign_item_inner( &mut self, link_name: Symbol, @@ -20,20 +20,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match link_name.as_str() { - // Threading - "pthread_condattr_setclock" => { - let [attr, clock_id] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_setclock(attr, clock_id)?; - this.write_scalar(result, dest)?; - } - "pthread_condattr_getclock" => { - let [attr, clock_id] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let result = this.pthread_condattr_getclock(attr, clock_id)?; - this.write_scalar(result, dest)?; - } - _ => return Ok(EmulateItemResult::NotSupported), } Ok(EmulateItemResult::NeedsJumping) diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 2c9208deb3..4623c40afd 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -4,31 +4,17 @@ use crate::concurrency::thread::MachineCallback; use crate::*; // pthread_mutexattr_t is either 4 or 8 bytes, depending on the platform. +// We ignore the platform layout and store our own fields: +// - kind: i32 -// Our chosen memory layout for emulation (does not have to match the platform layout!): -// store an i32 in the first four bytes equal to the corresponding libc mutex kind constant -// (e.g. PTHREAD_MUTEX_NORMAL). - -/// A flag that allows to distinguish `PTHREAD_MUTEX_NORMAL` from -/// `PTHREAD_MUTEX_DEFAULT`. Since in `glibc` they have the same numeric values, -/// but different behaviour, we need a way to distinguish them. We do this by -/// setting this bit flag to the `PTHREAD_MUTEX_NORMAL` mutexes. See the comment -/// in `pthread_mutexattr_settype` function. -const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000; - -fn is_mutex_kind_default<'mir, 'tcx: 'mir>( - ecx: &MiriInterpCx<'mir, 'tcx>, - kind: i32, -) -> InterpResult<'tcx, bool> { - Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")) -} - -fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( +#[inline] +fn mutexattr_kind_offset<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, - kind: i32, -) -> InterpResult<'tcx, bool> { - let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL"); - Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG)) +) -> InterpResult<'tcx, u64> { + Ok(match &*ecx.tcx.sess.target.os { + "linux" | "illumos" | "solaris" | "macos" => 0, + os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"), + }) } fn mutexattr_get_kind<'mir, 'tcx: 'mir>( @@ -37,7 +23,7 @@ fn mutexattr_get_kind<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( attr_op, - 0, + mutexattr_kind_offset(ecx)?, ecx.libc_ty_layout("pthread_mutexattr_t"), ecx.machine.layouts.i32, )? @@ -51,26 +37,57 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( attr_op, - 0, + mutexattr_kind_offset(ecx)?, Scalar::from_i32(kind), ecx.libc_ty_layout("pthread_mutexattr_t"), ecx.machine.layouts.i32, ) } +/// A flag that allows to distinguish `PTHREAD_MUTEX_NORMAL` from +/// `PTHREAD_MUTEX_DEFAULT`. Since in `glibc` they have the same numeric values, +/// but different behaviour, we need a way to distinguish them. We do this by +/// setting this bit flag to the `PTHREAD_MUTEX_NORMAL` mutexes. See the comment +/// in `pthread_mutexattr_settype` function. +const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000; + +fn is_mutex_kind_default<'mir, 'tcx: 'mir>( + ecx: &MiriInterpCx<'mir, 'tcx>, + kind: i32, +) -> InterpResult<'tcx, bool> { + Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")) +} + +fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( + ecx: &MiriInterpCx<'mir, 'tcx>, + kind: i32, +) -> InterpResult<'tcx, bool> { + let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL"); + Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG)) +} + // pthread_mutex_t is between 24 and 48 bytes, depending on the platform. +// We ignore the platform layout and store our own fields: +// - id: u32 +// - kind: i32 -// Our chosen memory layout for the emulated mutex (does not have to match the platform layout!): -// bytes 0-3: reserved for signature on macOS -// (need to avoid this because it is set by static initializer macros) -// bytes 4-7: mutex id as u32 or 0 if id is not assigned yet. -// bytes 12-15 or 16-19 (depending on platform): mutex kind, as an i32 -// (the kind has to be at this particular offset for compatibility with Linux's static initializer -// macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) +#[inline] +fn mutex_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { + // When adding a new OS, make sure its PTHREAD_MUTEX_INITIALIZER is 0 for this offset + // (and the `mutex_kind_offset` below). + Ok(match &*ecx.tcx.sess.target.os { + "linux" | "illumos" | "solaris" => 0, + // macOS stores a signature in the first bytes, so we have to move to offset 4. + "macos" => 4, + os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), + }) +} #[inline] -fn mutex_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { - if matches!(&*ecx.tcx.sess.target.os, "macos") { 4 } else { 0 } +fn mutex_kind_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { + // These offsets are picked for compatibility with Linux's static initializer + // macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) + if ecx.pointer_size().bytes() == 8 { 16 } else { 12 } } fn mutex_get_id<'mir, 'tcx: 'mir>( @@ -80,7 +97,7 @@ fn mutex_get_id<'mir, 'tcx: 'mir>( ecx.mutex_get_or_create_id( mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), - mutex_id_offset(ecx), + mutex_id_offset(ecx)?, ) } @@ -90,7 +107,7 @@ fn mutex_reset_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( mutex_op, - mutex_id_offset(ecx), + mutex_id_offset(ecx)?, Scalar::from_i32(0), ecx.libc_ty_layout("pthread_mutex_t"), ecx.machine.layouts.u32, @@ -101,10 +118,9 @@ fn mutex_get_kind<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, mutex_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, i32> { - let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; ecx.deref_pointer_and_read( mutex_op, - offset, + mutex_kind_offset(ecx), ecx.libc_ty_layout("pthread_mutex_t"), ecx.machine.layouts.i32, )? @@ -116,10 +132,9 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( mutex_op: &OpTy<'tcx, Provenance>, kind: i32, ) -> InterpResult<'tcx, ()> { - let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; ecx.deref_pointer_and_write( mutex_op, - offset, + mutex_kind_offset(ecx), Scalar::from_i32(kind), ecx.libc_ty_layout("pthread_mutex_t"), ecx.machine.layouts.i32, @@ -127,15 +142,18 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( } // pthread_rwlock_t is between 32 and 56 bytes, depending on the platform. - -// Our chosen memory layout for the emulated rwlock (does not have to match the platform layout!): -// bytes 0-3: reserved for signature on macOS -// (need to avoid this because it is set by static initializer macros) -// bytes 4-7: rwlock id as u32 or 0 if id is not assigned yet. +// We ignore the platform layout and store our own fields: +// - id: u32 #[inline] -fn rwlock_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { - if matches!(&*ecx.tcx.sess.target.os, "macos") { 4 } else { 0 } +fn rwlock_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { + // When adding a new OS, make sure its PTHREAD_RWLOCK_INITIALIZER is 0 for this offset. + Ok(match &*ecx.tcx.sess.target.os { + "linux" | "illumos" | "solaris" => 0, + // macOS stores a signature in the first bytes, so we have to move to offset 4. + "macos" => 4, + os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"), + }) } fn rwlock_get_id<'mir, 'tcx: 'mir>( @@ -145,17 +163,24 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>( ecx.rwlock_get_or_create_id( rwlock_op, ecx.libc_ty_layout("pthread_rwlock_t"), - rwlock_id_offset(ecx), + rwlock_id_offset(ecx)?, ) } -// pthread_condattr_t +// pthread_condattr_t. +// We ignore the platform layout and store our own fields: +// - clock: i32 -// Our chosen memory layout for emulation (does not have to match the platform layout!): -// store an i32 in the first four bytes equal to the corresponding libc clock id constant -// (e.g. CLOCK_REALTIME). - -const CONDATTR_CLOCK_OFFSET: u64 = 0; +#[inline] +fn condattr_clock_offset<'mir, 'tcx: 'mir>( + ecx: &MiriInterpCx<'mir, 'tcx>, +) -> InterpResult<'tcx, u64> { + Ok(match &*ecx.tcx.sess.target.os { + "linux" | "illumos" | "solaris" => 0, + // macOS does not have a clock attribute. + os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"), + }) +} fn condattr_get_clock_id<'mir, 'tcx: 'mir>( ecx: &MiriInterpCx<'mir, 'tcx>, @@ -163,7 +188,7 @@ fn condattr_get_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( attr_op, - CONDATTR_CLOCK_OFFSET, + condattr_clock_offset(ecx)?, ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, )? @@ -177,34 +202,43 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( attr_op, - CONDATTR_CLOCK_OFFSET, + condattr_clock_offset(ecx)?, Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, ) } -// pthread_cond_t - -// Our chosen memory layout for the emulated conditional variable (does not have -// to match the platform layout!): - -// bytes 0-3: reserved for signature on macOS -// bytes 4-7: the conditional variable id as u32 or 0 if id is not assigned yet. -// bytes 8-11: the clock id constant as i32 - -const CONDVAR_CLOCK_OFFSET: u64 = 8; +// pthread_cond_t. +// We ignore the platform layout and store our own fields: +// - id: u32 +// - clock: i32 #[inline] -fn cond_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { - if matches!(&*ecx.tcx.sess.target.os, "macos") { 4 } else { 0 } +fn cond_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { + // When adding a new OS, make sure its PTHREAD_COND_INITIALIZER is 0 for this offset + // (and the `COND_CLOCK_OFFSET` below is initialized to `CLOCK_REALTIME`). + Ok(match &*ecx.tcx.sess.target.os { + "linux" | "illumos" | "solaris" => 0, + // macOS stores a signature in the first bytes, so we have to move to offset 4. + "macos" => 4, + os => throw_unsup_format!("`pthread_cond` is not supported on {os}"), + }) } +// macOS doesn't have a clock attribute, but to keep the code uniform we store +// a clock ID in the pthread_cond_t anyway. There's enough space. +const COND_CLOCK_OFFSET: u64 = 8; + fn cond_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, cond_op: &OpTy<'tcx, Provenance>, ) -> InterpResult<'tcx, CondvarId> { - ecx.condvar_get_or_create_id(cond_op, ecx.libc_ty_layout("pthread_cond_t"), cond_id_offset(ecx)) + ecx.condvar_get_or_create_id( + cond_op, + ecx.libc_ty_layout("pthread_cond_t"), + cond_id_offset(ecx)?, + ) } fn cond_reset_id<'mir, 'tcx: 'mir>( @@ -213,7 +247,7 @@ fn cond_reset_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, - cond_id_offset(ecx), + cond_id_offset(ecx)?, Scalar::from_i32(0), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.u32, @@ -226,7 +260,7 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( cond_op, - CONDVAR_CLOCK_OFFSET, + COND_CLOCK_OFFSET, ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, )? @@ -240,7 +274,7 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, - CONDVAR_CLOCK_OFFSET, + COND_CLOCK_OFFSET, Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, @@ -307,13 +341,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_mutexattr_init` is not supported on {}", - this.tcx.sess.target.os - ); - } - let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"); mutexattr_set_kind(this, attr_op, default_kind)?; @@ -396,13 +423,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_mutex_init` is not supported on {}", - this.tcx.sess.target.os - ); - } - let attr = this.read_pointer(attr_op)?; let kind = if this.ptr_is_null(attr)? { this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") @@ -557,13 +577,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_rwlock_rdlock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -582,13 +595,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_rwlock_tryrdlock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -606,13 +612,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_rwlock_wrlock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -643,13 +642,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_rwlock_trywrlock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -667,13 +659,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_rwlock_unlock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let id = rwlock_get_id(this, rwlock_op)?; let active_thread = this.get_active_thread(); @@ -693,13 +678,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_rwlock_destroy` is not supported on {}", - this.tcx.sess.target.os - ); - } - let id = rwlock_get_id(this, rwlock_op)?; if this.rwlock_is_locked(id) { @@ -724,19 +702,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_condattr_init` is not supported on {}", - this.tcx.sess.target.os - ); + // no clock attribute on macOS + if this.tcx.sess.target.os != "macos" { + // The default value of the clock attribute shall refer to the system + // clock. + // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html + let default_clock_id = this.eval_libc_i32("CLOCK_REALTIME"); + condattr_set_clock_id(this, attr_op, default_clock_id)?; } - // The default value of the clock attribute shall refer to the system - // clock. - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_condattr_setclock.html - let default_clock_id = this.eval_libc_i32("CLOCK_REALTIME"); - condattr_set_clock_id(this, attr_op, default_clock_id)?; - Ok(0) } @@ -747,14 +721,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // Does not exist on macOS! - if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_condattr_setclock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let clock_id = this.read_scalar(clock_id_op)?.to_i32()?; if clock_id == this.eval_libc_i32("CLOCK_REALTIME") || clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") @@ -775,14 +741,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - // Does not exist on macOS! - if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_condattr_getclock` is not supported on {}", - this.tcx.sess.target.os - ); - } - let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?; @@ -796,8 +754,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); // Destroying an uninit pthread_condattr is UB, so check to make sure it's not uninit. - condattr_get_clock_id(this, attr_op)?; + // There's no clock attribute on macOS. + if this.tcx.sess.target.os != "macos" { + condattr_get_clock_id(this, attr_op)?; + } + // De-init the entire thing. // This might lead to false positives, see comment in pthread_mutexattr_destroy this.write_uninit( &this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?, @@ -813,15 +775,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "solaris" | "illumos") { - throw_unsup_format!( - "`pthread_cond_init` is not supported on {}", - this.tcx.sess.target.os - ); - } - let attr = this.read_pointer(attr_op)?; - let clock_id = if this.ptr_is_null(attr)? { + // Default clock if att is null, and on macOS where there is no clock attribute. + let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" { this.eval_libc_i32("CLOCK_REALTIME") } else { condattr_get_clock_id(this, attr_op)? diff --git a/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs b/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs index 13e639a867..b5427d55eb 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs +++ b/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs @@ -1,4 +1,5 @@ //@ignore-target-windows: No libc on Windows +//@ignore-target-apple: Our macOS condattr don't have any fields so we do not notice this. /// Test that destroying a pthread_condattr twice fails, even without a check for number validity From 1c376b97810d553ac7b1172b1569f16bdb6630b6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 11:08:58 +0200 Subject: [PATCH 167/208] sanity-check the pthread offsets against the static initializers --- src/helpers.rs | 9 ++- src/shims/unix/sync.rs | 140 ++++++++++++++++++++++++++++++++++------- 2 files changed, 123 insertions(+), 26 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index f81e997efd..40c2008ac9 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -255,14 +255,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Evaluates the scalar at the specified path. - fn eval_path_scalar(&self, path: &[&str]) -> Scalar { + fn eval_path(&self, path: &[&str]) -> OpTy<'tcx, Provenance> { let this = self.eval_context_ref(); let instance = this.resolve_path(path, Namespace::ValueNS); // We don't give a span -- this isn't actually used directly by the program anyway. let const_val = this.eval_global(instance).unwrap_or_else(|err| { panic!("failed to evaluate required Rust item: {path:?}\n{err:?}") }); - this.read_scalar(&const_val) + const_val.into() + } + fn eval_path_scalar(&self, path: &[&str]) -> Scalar { + let this = self.eval_context_ref(); + let val = this.eval_path(path); + this.read_scalar(&val) .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}")) } diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 4623c40afd..f24f279ab0 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -1,5 +1,8 @@ +use std::sync::atomic::{AtomicBool, Ordering}; use std::time::SystemTime; +use rustc_target::abi::Size; + use crate::concurrency::thread::MachineCallback; use crate::*; @@ -71,23 +74,54 @@ fn is_mutex_kind_normal<'mir, 'tcx: 'mir>( // - id: u32 // - kind: i32 -#[inline] fn mutex_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { - // When adding a new OS, make sure its PTHREAD_MUTEX_INITIALIZER is 0 for this offset - // (and the `mutex_kind_offset` below). - Ok(match &*ecx.tcx.sess.target.os { + let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"), - }) + }; + + // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once): + // the id must start out as 0. + static SANITY: AtomicBool = AtomicBool::new(false); + if !SANITY.swap(true, Ordering::Relaxed) { + let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]); + let id_field = static_initializer + .offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx) + .unwrap(); + let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap(); + assert_eq!( + id, 0, + "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: id is not 0" + ); + } + + Ok(offset) } -#[inline] fn mutex_kind_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { // These offsets are picked for compatibility with Linux's static initializer // macros, e.g. PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP.) - if ecx.pointer_size().bytes() == 8 { 16 } else { 12 } + let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 }; + + // Sanity-check this against PTHREAD_MUTEX_INITIALIZER (but only once): + // the kind must start out as PTHREAD_MUTEX_DEFAULT. + static SANITY: AtomicBool = AtomicBool::new(false); + if !SANITY.swap(true, Ordering::Relaxed) { + let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]); + let kind_field = static_initializer + .offset(Size::from_bytes(mutex_kind_offset(ecx)), ecx.machine.layouts.i32, ecx) + .unwrap(); + let kind = ecx.read_scalar(&kind_field).unwrap().to_i32().unwrap(); + assert_eq!( + kind, + ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"), + "PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: kind is not PTHREAD_MUTEX_DEFAULT" + ); + } + + offset } fn mutex_get_id<'mir, 'tcx: 'mir>( @@ -108,7 +142,7 @@ fn mutex_reset_id<'mir, 'tcx: 'mir>( ecx.deref_pointer_and_write( mutex_op, mutex_id_offset(ecx)?, - Scalar::from_i32(0), + Scalar::from_u32(0), ecx.libc_ty_layout("pthread_mutex_t"), ecx.machine.layouts.u32, ) @@ -145,15 +179,30 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( // We ignore the platform layout and store our own fields: // - id: u32 -#[inline] fn rwlock_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { - // When adding a new OS, make sure its PTHREAD_RWLOCK_INITIALIZER is 0 for this offset. - Ok(match &*ecx.tcx.sess.target.os { + let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"), - }) + }; + + // Sanity-check this against PTHREAD_RWLOCK_INITIALIZER (but only once): + // the id must start out as 0. + static SANITY: AtomicBool = AtomicBool::new(false); + if !SANITY.swap(true, Ordering::Relaxed) { + let static_initializer = ecx.eval_path(&["libc", "PTHREAD_RWLOCK_INITIALIZER"]); + let id_field = static_initializer + .offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx) + .unwrap(); + let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap(); + assert_eq!( + id, 0, + "PTHREAD_RWLOCK_INITIALIZER is incompatible with our pthread_rwlock layout: id is not 0" + ); + } + + Ok(offset) } fn rwlock_get_id<'mir, 'tcx: 'mir>( @@ -214,21 +263,64 @@ fn condattr_set_clock_id<'mir, 'tcx: 'mir>( // - id: u32 // - clock: i32 -#[inline] fn cond_id_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx, u64> { - // When adding a new OS, make sure its PTHREAD_COND_INITIALIZER is 0 for this offset - // (and the `COND_CLOCK_OFFSET` below is initialized to `CLOCK_REALTIME`). - Ok(match &*ecx.tcx.sess.target.os { + let offset = match &*ecx.tcx.sess.target.os { "linux" | "illumos" | "solaris" => 0, // macOS stores a signature in the first bytes, so we have to move to offset 4. "macos" => 4, os => throw_unsup_format!("`pthread_cond` is not supported on {os}"), - }) + }; + + // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once): + // the id must start out as 0. + static SANITY: AtomicBool = AtomicBool::new(false); + if !SANITY.swap(true, Ordering::Relaxed) { + let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]); + let id_field = static_initializer + .offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx) + .unwrap(); + let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap(); + assert_eq!( + id, 0, + "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: id is not 0" + ); + } + + Ok(offset) +} + +/// Determines whether this clock represents the real-time clock, CLOCK_REALTIME. +fn is_cond_clock_realtime<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>, clock_id: i32) -> bool { + // To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms, + // we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER + // makes the clock 0 but CLOCK_REALTIME is 3. + // However, we need to always be able to distinguish this from CLOCK_MONOTONIC. + clock_id == ecx.eval_libc_i32("CLOCK_REALTIME") + || (clock_id == 0 && clock_id != ecx.eval_libc_i32("CLOCK_MONOTONIC")) } -// macOS doesn't have a clock attribute, but to keep the code uniform we store -// a clock ID in the pthread_cond_t anyway. There's enough space. -const COND_CLOCK_OFFSET: u64 = 8; +fn cond_clock_offset<'mir, 'tcx: 'mir>(ecx: &MiriInterpCx<'mir, 'tcx>) -> u64 { + // macOS doesn't have a clock attribute, but to keep the code uniform we store + // a clock ID in the pthread_cond_t anyway. There's enough space. + let offset = 8; + + // Sanity-check this against PTHREAD_COND_INITIALIZER (but only once): + // the clock must start out as CLOCK_REALTIME. + static SANITY: AtomicBool = AtomicBool::new(false); + if !SANITY.swap(true, Ordering::Relaxed) { + let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]); + let id_field = static_initializer + .offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx) + .unwrap(); + let id = ecx.read_scalar(&id_field).unwrap().to_i32().unwrap(); + assert!( + is_cond_clock_realtime(ecx, id), + "PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME" + ); + } + + offset +} fn cond_get_id<'mir, 'tcx: 'mir>( ecx: &mut MiriInterpCx<'mir, 'tcx>, @@ -260,7 +352,7 @@ fn cond_get_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, i32> { ecx.deref_pointer_and_read( cond_op, - COND_CLOCK_OFFSET, + cond_clock_offset(ecx), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, )? @@ -274,7 +366,7 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( ) -> InterpResult<'tcx, ()> { ecx.deref_pointer_and_write( cond_op, - COND_CLOCK_OFFSET, + cond_clock_offset(ecx), Scalar::from_i32(clock_id), ecx.libc_ty_layout("pthread_cond_t"), ecx.machine.layouts.i32, @@ -776,7 +868,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let attr = this.read_pointer(attr_op)?; - // Default clock if att is null, and on macOS where there is no clock attribute. + // Default clock if `attr` is null, and on macOS where there is no clock attribute. let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" { this.eval_libc_i32("CLOCK_REALTIME") } else { @@ -858,7 +950,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } }; - let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME") { + let timeout_time = if is_cond_clock_realtime(this, clock_id) { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; CallbackTime::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap()) } else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") { From f09547cef7b4bbd6d2bdb9ff2cb48bcfc66fb42c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 15:11:49 +0200 Subject: [PATCH 168/208] unix/thread: properly use pthread_t for thread IDs --- ci/ci.sh | 2 +- src/shims/unix/thread.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index 8b7d23621a..03a387d43c 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -146,7 +146,7 @@ case $HOST_TARGET in MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic - MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync + MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync # TODO fix solaris stack guard # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm diff --git a/src/shims/unix/thread.rs b/src/shims/unix/thread.rs index 7f814e080f..9e09401a81 100644 --- a/src/shims/unix/thread.rs +++ b/src/shims/unix/thread.rs @@ -51,7 +51,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { fn pthread_detach(&mut self, thread: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> { let this = self.eval_context_mut(); - let thread_id = this.read_target_usize(thread)?; + let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?; this.detach_thread( thread_id.try_into().expect("thread ID should fit in u32"), /*allow_terminated_joined*/ false, @@ -64,7 +64,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); let thread_id = this.get_active_thread(); - Ok(Scalar::from_target_usize(thread_id.into(), this)) + Ok(Scalar::from_uint(thread_id.to_u32(), this.libc_ty_layout("pthread_t").size)) } /// Set the name of the current thread. `max_name_len` is the maximal length of the name @@ -77,7 +77,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let thread = ThreadId::try_from(thread.to_target_usize(this)?).unwrap(); + let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; + let thread = ThreadId::try_from(thread).unwrap(); let name = name.to_pointer(this)?; let name = this.read_c_str(name)?.to_owned(); @@ -100,7 +101,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let thread = ThreadId::try_from(thread.to_target_usize(this)?).unwrap(); + let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?; + let thread = ThreadId::try_from(thread).unwrap(); let name_out = name_out.to_pointer(this)?; let len = len.to_target_usize(this)?; From 7b49d2155b5d28cef09d772d9a2a5c908075d7ab Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 15:31:13 +0200 Subject: [PATCH 169/208] housekeeping: update dependencies --- Cargo.lock | 244 ++++++++++++++++++----------------- cargo-miri/Cargo.lock | 187 +++++++++++---------------- test_dependencies/Cargo.lock | 213 +++++++++++++++--------------- 3 files changed, 309 insertions(+), 335 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6960c1661c..417ecb09b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -58,21 +58,21 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -85,21 +85,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "regex-automata", @@ -117,9 +111,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -140,9 +134,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.86" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" [[package]] name = "cfg-if" @@ -150,6 +144,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.38" @@ -193,9 +193,9 @@ dependencies = [ [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -258,9 +258,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -283,9 +283,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345" dependencies = [ "nix", "windows-sys 0.52.0", @@ -340,9 +340,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "generic-array" @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -410,9 +410,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jemalloc-sys" @@ -438,9 +438,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libffi" @@ -463,12 +463,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.52.5", ] [[package]] @@ -477,7 +477,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags", "libc", ] @@ -489,9 +489,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -499,9 +499,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "measureme" @@ -519,9 +519,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -567,20 +567,21 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.4.2", + "bitflags", "cfg-if", + "cfg_aliases", "libc", ] [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -629,9 +630,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -639,22 +640,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "parse-zoneinfo" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" dependencies = [ "regex", ] @@ -708,9 +709,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "portable-atomic" @@ -736,18 +737,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -784,11 +785,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -804,9 +805,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -816,9 +817,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -827,9 +828,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-demangle" @@ -866,11 +867,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -900,18 +901,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -920,9 +921,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -946,15 +947,15 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.50" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -963,9 +964,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -975,18 +976,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", @@ -1085,9 +1086,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "valuable" @@ -1144,7 +1145,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.5", ] [[package]] @@ -1164,17 +1165,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -1185,9 +1187,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -1197,9 +1199,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -1209,9 +1211,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -1221,9 +1229,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -1233,9 +1241,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -1245,9 +1253,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -1257,9 +1265,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "yansi-term" diff --git a/cargo-miri/Cargo.lock b/cargo-miri/Cargo.lock index a1dbc85605..b8ead46024 100644 --- a/cargo-miri/Cargo.lock +++ b/cargo-miri/Cargo.lock @@ -4,21 +4,15 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "camino" @@ -44,9 +38,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694c8807f2ae16faecc43dc17d74b3eb042482789fd0eb64b39a2e04e087053f" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -104,15 +98,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", @@ -121,25 +115,24 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.2", + "bitflags", "libc", - "redox_syscall", ] [[package]] @@ -156,36 +149,27 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -194,9 +178,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9bf37423495cd3a6a9ef8c75fc4566de0d26de0ab75f36f67a426e58df5768c" +checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0" dependencies = [ "anyhow", "rustc_version", @@ -221,11 +205,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.31" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -258,18 +242,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.197" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -278,9 +262,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -289,9 +273,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -300,9 +284,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -312,18 +296,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", @@ -338,9 +322,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -352,37 +336,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" @@ -398,7 +360,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.3", + "windows-targets 0.52.5", ] [[package]] @@ -418,17 +380,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.3", - "windows_aarch64_msvc 0.52.3", - "windows_i686_gnu 0.52.3", - "windows_i686_msvc 0.52.3", - "windows_x86_64_gnu 0.52.3", - "windows_x86_64_gnullvm 0.52.3", - "windows_x86_64_msvc 0.52.3", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -439,9 +402,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -451,9 +414,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -463,9 +426,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.3" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -475,9 +444,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -487,9 +456,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -499,9 +468,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -511,6 +480,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.3" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/test_dependencies/Cargo.lock b/test_dependencies/Cargo.lock index 11d430bad1..3dd5eb9b91 100644 --- a/test_dependencies/Cargo.lock +++ b/test_dependencies/Cargo.lock @@ -19,15 +19,15 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -40,36 +40,27 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" [[package]] name = "cfg-if" @@ -89,9 +80,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "getrandom" @@ -106,9 +97,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -125,36 +116,36 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "libc" -version = "0.2.151" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -162,21 +153,21 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] @@ -197,7 +188,7 @@ name = "miri-test-deps" version = "0.1.0" dependencies = [ "getrandom 0.1.16", - "getrandom 0.2.11", + "getrandom 0.2.14", "libc", "num_cpus", "page_size", @@ -244,9 +235,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -254,22 +245,22 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "ppv-lite86" @@ -279,18 +270,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -322,16 +313,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.14", ] [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] @@ -342,11 +333,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -361,34 +352,34 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -397,22 +388,21 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] [[package]] name = "tokio" -version = "1.35.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -458,9 +448,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -468,9 +458,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -483,9 +473,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -493,9 +483,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -506,9 +496,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "winapi" @@ -547,7 +537,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -567,17 +557,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -588,9 +579,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -600,9 +591,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -612,9 +603,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -624,9 +621,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -636,9 +633,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -648,9 +645,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -660,6 +657,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" From e761a63b6c7552ea17ee489fb82136dadac0d8a2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 13:59:50 +0200 Subject: [PATCH 170/208] move things out of libc-misc that have a better home; make libc-misc work on illumos --- ci/ci.sh | 4 +- src/shims/unix/solarish/foreign_items.rs | 8 +- tests/pass-dep/shims/libc-fs.rs | 174 ++++++++++- tests/pass-dep/shims/libc-misc.rs | 365 ++++++----------------- tests/pass-dep/shims/libc-time.rs | 93 ++++++ tests/pass-dep/shims/posix_memalign.rs | 82 ----- 6 files changed, 362 insertions(+), 364 deletions(-) create mode 100644 tests/pass-dep/shims/libc-time.rs delete mode 100644 tests/pass-dep/shims/posix_memalign.rs diff --git a/ci/ci.sh b/ci/ci.sh index 03a387d43c..6881f3695d 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -146,8 +146,8 @@ case $HOST_TARGET in MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic - MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync - # TODO fix solaris stack guard + MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc + # TODO fix solaris stack guard # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm diff --git a/src/shims/unix/solarish/foreign_items.rs b/src/shims/unix/solarish/foreign_items.rs index 16a0472756..c01ae0ecb9 100644 --- a/src/shims/unix/solarish/foreign_items.rs +++ b/src/shims/unix/solarish/foreign_items.rs @@ -10,7 +10,6 @@ pub fn is_dyn_sym(_name: &str) -> bool { impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - #[allow(warnings)] fn emulate_foreign_item_inner( &mut self, link_name: Symbol, @@ -20,6 +19,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); match link_name.as_str() { + // Miscellaneous + "___errno" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let errno_place = this.last_error_place()?; + this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), } Ok(EmulateItemResult::NeedsJumping) diff --git a/tests/pass-dep/shims/libc-fs.rs b/tests/pass-dep/shims/libc-fs.rs index 4cfc1843a7..0dd849a045 100644 --- a/tests/pass-dep/shims/libc-fs.rs +++ b/tests/pass-dep/shims/libc-fs.rs @@ -4,10 +4,11 @@ #![feature(io_error_more)] #![feature(io_error_uncategorized)] -use std::ffi::CString; +use std::ffi::{CStr, CString, OsString}; use std::fs::{canonicalize, remove_dir_all, remove_file, File}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; +use std::os::unix::io::AsRawFd; use std::path::PathBuf; #[path = "../../utils/mod.rs"] @@ -27,6 +28,14 @@ fn main() { #[cfg(target_os = "linux")] test_o_tmpfile_flag(); test_posix_mkstemp(); + test_posix_realpath_alloc(); + test_posix_realpath_noalloc(); + test_posix_realpath_errors(); + #[cfg(target_os = "linux")] + test_posix_fadvise(); + #[cfg(target_os = "linux")] + test_sync_file_range(); + test_isatty(); } /// Prepare: compute filename and make sure the file does not exist. @@ -256,3 +265,166 @@ fn test_posix_mkstemp() { assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); } } + +/// Test allocating variant of `realpath`. +fn test_posix_realpath_alloc() { + use std::os::unix::ffi::OsStrExt; + use std::os::unix::ffi::OsStringExt; + + let buf; + let path = utils::tmp().join("miri_test_libc_posix_realpath_alloc"); + let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + + // Cleanup before test. + remove_file(&path).ok(); + // Create file. + drop(File::create(&path).unwrap()); + unsafe { + let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut()); + assert!(!r.is_null()); + buf = CStr::from_ptr(r).to_bytes().to_vec(); + libc::free(r as *mut _); + } + let canonical = PathBuf::from(OsString::from_vec(buf)); + assert_eq!(path.file_name(), canonical.file_name()); + + // Cleanup after test. + remove_file(&path).unwrap(); +} + +/// Test non-allocating variant of `realpath`. +fn test_posix_realpath_noalloc() { + use std::ffi::{CStr, CString}; + use std::os::unix::ffi::OsStrExt; + + let path = utils::tmp().join("miri_test_libc_posix_realpath_noalloc"); + let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); + + let mut v = vec![0; libc::PATH_MAX as usize]; + + // Cleanup before test. + remove_file(&path).ok(); + // Create file. + drop(File::create(&path).unwrap()); + unsafe { + let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr()); + assert!(!r.is_null()); + } + let c = unsafe { CStr::from_ptr(v.as_ptr()) }; + let canonical = PathBuf::from(c.to_str().expect("CStr to str")); + + assert_eq!(path.file_name(), canonical.file_name()); + + // Cleanup after test. + remove_file(&path).unwrap(); +} + +/// Test failure cases for `realpath`. +fn test_posix_realpath_errors() { + use std::ffi::CString; + use std::io::ErrorKind; + + // Test nonexistent path returns an error. + let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed"); + let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) }; + assert!(r.is_null()); + let e = std::io::Error::last_os_error(); + assert_eq!(e.raw_os_error(), Some(libc::ENOENT)); + assert_eq!(e.kind(), ErrorKind::NotFound); +} + +#[cfg(target_os = "linux")] +fn test_posix_fadvise() { + use std::io::Write; + + let path = utils::tmp().join("miri_test_libc_posix_fadvise.txt"); + // Cleanup before test + remove_file(&path).ok(); + + // Set up an open file + let mut file = File::create(&path).unwrap(); + let bytes = b"Hello, World!\n"; + file.write(bytes).unwrap(); + + // Test calling posix_fadvise on a file. + let result = unsafe { + libc::posix_fadvise( + file.as_raw_fd(), + 0, + bytes.len().try_into().unwrap(), + libc::POSIX_FADV_DONTNEED, + ) + }; + drop(file); + remove_file(&path).unwrap(); + assert_eq!(result, 0); +} + +#[cfg(target_os = "linux")] +fn test_sync_file_range() { + use std::io::Write; + + let path = utils::tmp().join("miri_test_libc_sync_file_range.txt"); + // Cleanup before test. + remove_file(&path).ok(); + + // Write to a file. + let mut file = File::create(&path).unwrap(); + let bytes = b"Hello, World!\n"; + file.write(bytes).unwrap(); + + // Test calling sync_file_range on the file. + let result_1 = unsafe { + libc::sync_file_range( + file.as_raw_fd(), + 0, + 0, + libc::SYNC_FILE_RANGE_WAIT_BEFORE + | libc::SYNC_FILE_RANGE_WRITE + | libc::SYNC_FILE_RANGE_WAIT_AFTER, + ) + }; + drop(file); + + // Test calling sync_file_range on a file opened for reading. + let file = File::open(&path).unwrap(); + let result_2 = unsafe { + libc::sync_file_range( + file.as_raw_fd(), + 0, + 0, + libc::SYNC_FILE_RANGE_WAIT_BEFORE + | libc::SYNC_FILE_RANGE_WRITE + | libc::SYNC_FILE_RANGE_WAIT_AFTER, + ) + }; + drop(file); + + remove_file(&path).unwrap(); + assert_eq!(result_1, 0); + assert_eq!(result_2, 0); +} + +fn test_isatty() { + // Testing whether our isatty shim returns the right value would require controlling whether + // these streams are actually TTYs, which is hard. + // For now, we just check that these calls are supported at all. + unsafe { + libc::isatty(libc::STDIN_FILENO); + libc::isatty(libc::STDOUT_FILENO); + libc::isatty(libc::STDERR_FILENO); + + // But when we open a file, it is definitely not a TTY. + let path = utils::tmp().join("notatty.txt"); + // Cleanup before test. + remove_file(&path).ok(); + let file = File::create(&path).unwrap(); + + assert_eq!(libc::isatty(file.as_raw_fd()), 0); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY); + + // Cleanup after test. + drop(file); + remove_file(&path).unwrap(); + } +} diff --git a/tests/pass-dep/shims/libc-misc.rs b/tests/pass-dep/shims/libc-misc.rs index f710daf527..9644920c49 100644 --- a/tests/pass-dep/shims/libc-misc.rs +++ b/tests/pass-dep/shims/libc-misc.rs @@ -1,158 +1,16 @@ //@ignore-target-windows: No libc on Windows //@compile-flags: -Zmiri-disable-isolation #![feature(io_error_more)] +#![feature(pointer_is_aligned_to)] +#![feature(strict_provenance)] -use std::fs::{remove_file, File}; -use std::mem::transmute; -use std::os::unix::io::AsRawFd; -use std::path::PathBuf; - -#[path = "../../utils/mod.rs"] -mod utils; - -/// Test allocating variant of `realpath`. -fn test_posix_realpath_alloc() { - use std::ffi::OsString; - use std::ffi::{CStr, CString}; - use std::os::unix::ffi::OsStrExt; - use std::os::unix::ffi::OsStringExt; - - let buf; - let path = utils::tmp().join("miri_test_libc_posix_realpath_alloc"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); - - // Cleanup before test. - remove_file(&path).ok(); - // Create file. - drop(File::create(&path).unwrap()); - unsafe { - let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut()); - assert!(!r.is_null()); - buf = CStr::from_ptr(r).to_bytes().to_vec(); - libc::free(r as *mut _); - } - let canonical = PathBuf::from(OsString::from_vec(buf)); - assert_eq!(path.file_name(), canonical.file_name()); - - // Cleanup after test. - remove_file(&path).unwrap(); -} - -/// Test non-allocating variant of `realpath`. -fn test_posix_realpath_noalloc() { - use std::ffi::{CStr, CString}; - use std::os::unix::ffi::OsStrExt; - - let path = utils::tmp().join("miri_test_libc_posix_realpath_noalloc"); - let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); - - let mut v = vec![0; libc::PATH_MAX as usize]; - - // Cleanup before test. - remove_file(&path).ok(); - // Create file. - drop(File::create(&path).unwrap()); - unsafe { - let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr()); - assert!(!r.is_null()); - } - let c = unsafe { CStr::from_ptr(v.as_ptr()) }; - let canonical = PathBuf::from(c.to_str().expect("CStr to str")); - - assert_eq!(path.file_name(), canonical.file_name()); - - // Cleanup after test. - remove_file(&path).unwrap(); -} - -/// Test failure cases for `realpath`. -fn test_posix_realpath_errors() { - use std::ffi::CString; - use std::io::ErrorKind; - - // Test nonexistent path returns an error. - let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed"); - let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) }; - assert!(r.is_null()); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::ENOENT)); - assert_eq!(e.kind(), ErrorKind::NotFound); -} - -#[cfg(target_os = "linux")] -fn test_posix_fadvise() { - use std::io::Write; - - let path = utils::tmp().join("miri_test_libc_posix_fadvise.txt"); - // Cleanup before test - remove_file(&path).ok(); - - // Set up an open file - let mut file = File::create(&path).unwrap(); - let bytes = b"Hello, World!\n"; - file.write(bytes).unwrap(); - - // Test calling posix_fadvise on a file. - let result = unsafe { - libc::posix_fadvise( - file.as_raw_fd(), - 0, - bytes.len().try_into().unwrap(), - libc::POSIX_FADV_DONTNEED, - ) - }; - drop(file); - remove_file(&path).unwrap(); - assert_eq!(result, 0); -} - -#[cfg(target_os = "linux")] -fn test_sync_file_range() { - use std::io::Write; - - let path = utils::tmp().join("miri_test_libc_sync_file_range.txt"); - // Cleanup before test. - remove_file(&path).ok(); - - // Write to a file. - let mut file = File::create(&path).unwrap(); - let bytes = b"Hello, World!\n"; - file.write(bytes).unwrap(); - - // Test calling sync_file_range on the file. - let result_1 = unsafe { - libc::sync_file_range( - file.as_raw_fd(), - 0, - 0, - libc::SYNC_FILE_RANGE_WAIT_BEFORE - | libc::SYNC_FILE_RANGE_WRITE - | libc::SYNC_FILE_RANGE_WAIT_AFTER, - ) - }; - drop(file); - - // Test calling sync_file_range on a file opened for reading. - let file = File::open(&path).unwrap(); - let result_2 = unsafe { - libc::sync_file_range( - file.as_raw_fd(), - 0, - 0, - libc::SYNC_FILE_RANGE_WAIT_BEFORE - | libc::SYNC_FILE_RANGE_WRITE - | libc::SYNC_FILE_RANGE_WAIT_AFTER, - ) - }; - drop(file); - - remove_file(&path).unwrap(); - assert_eq!(result_1, 0); - assert_eq!(result_2, 0); -} +use std::mem::{self, transmute}; +use std::ptr; /// Tests whether each thread has its own `__errno_location`. fn test_thread_local_errno() { + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + use libc::___errno as __errno_location; #[cfg(target_os = "linux")] use libc::__errno_location; #[cfg(any(target_os = "macos", target_os = "freebsd"))] @@ -171,116 +29,6 @@ fn test_thread_local_errno() { } } -/// Tests whether clock support exists at all -fn test_clocks() { - let mut tp = std::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); - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - { - 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); - } - #[cfg(target_os = "macos")] - { - let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) }; - assert_eq!(is_error, 0); - } -} - -fn test_posix_gettimeofday() { - let mut tp = std::mem::MaybeUninit::::uninit(); - let tz = std::ptr::null_mut::(); - #[cfg(target_os = "macos")] // `tz` has a different type on macOS - let tz = tz as *mut libc::c_void; - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz) }; - assert_eq!(is_error, 0); - let tv = unsafe { tp.assume_init() }; - assert!(tv.tv_sec > 0); - assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - - // Test that non-null tz returns an error. - let mut tz = std::mem::MaybeUninit::::uninit(); - let tz_ptr = tz.as_mut_ptr(); - #[cfg(target_os = "macos")] // `tz` has a different type on macOS - let tz_ptr = tz_ptr as *mut libc::c_void; - let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr) }; - assert_eq!(is_error, -1); -} - -fn test_localtime_r() { - use std::ffi::CStr; - use std::{env, ptr}; - - // Set timezone to GMT. - let key = "TZ"; - env::set_var(key, "GMT"); - - const TIME_SINCE_EPOCH: libc::time_t = 1712475836; - let custom_time_ptr = &TIME_SINCE_EPOCH; - let mut tm = libc::tm { - tm_sec: 0, - tm_min: 0, - tm_hour: 0, - tm_mday: 0, - tm_mon: 0, - tm_year: 0, - tm_wday: 0, - tm_yday: 0, - tm_isdst: 0, - tm_gmtoff: 0, - tm_zone: std::ptr::null_mut::(), - }; - let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) }; - - assert_eq!(tm.tm_sec, 56); - assert_eq!(tm.tm_min, 43); - assert_eq!(tm.tm_hour, 7); - assert_eq!(tm.tm_mday, 7); - assert_eq!(tm.tm_mon, 3); - assert_eq!(tm.tm_year, 124); - assert_eq!(tm.tm_wday, 0); - assert_eq!(tm.tm_yday, 97); - assert_eq!(tm.tm_isdst, -1); - assert_eq!(tm.tm_gmtoff, 0); - unsafe { assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") }; - - // The returned value is the pointer passed in. - assert!(ptr::eq(res, &mut tm)); - - //Remove timezone setting. - env::remove_var(key); -} - -fn test_isatty() { - // Testing whether our isatty shim returns the right value would require controlling whether - // these streams are actually TTYs, which is hard. - // For now, we just check that these calls are supported at all. - unsafe { - libc::isatty(libc::STDIN_FILENO); - libc::isatty(libc::STDOUT_FILENO); - libc::isatty(libc::STDERR_FILENO); - - // But when we open a file, it is definitely not a TTY. - let path = utils::tmp().join("notatty.txt"); - // Cleanup before test. - remove_file(&path).ok(); - let file = File::create(&path).unwrap(); - - assert_eq!(libc::isatty(file.as_raw_fd()), 0); - assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY); - - // Cleanup after test. - drop(file); - remove_file(&path).unwrap(); - } -} - fn test_memcpy() { unsafe { let src = [1i8, 2, 3]; @@ -306,7 +54,7 @@ fn test_memcpy() { libc::memcpy( &mut dest as *mut i32 as *mut libc::c_void, &src as *const i32 as *const libc::c_void, - std::mem::size_of::(), + mem::size_of::(), ); assert_eq!(dest, src); } @@ -317,7 +65,7 @@ fn test_memcpy() { libc::memcpy( &mut dest as *mut Option as *mut libc::c_void, &src as *const Option as *const libc::c_void, - std::mem::size_of::>(), + mem::size_of::>(), ); assert_eq!(dest, src); } @@ -328,7 +76,7 @@ fn test_memcpy() { libc::memcpy( &mut dest as *mut &'static i32 as *mut libc::c_void, &src as *const &'static i32 as *const libc::c_void, - std::mem::size_of::<&'static i32>(), + mem::size_of::<&'static i32>(), ); assert_eq!(*dest, 123); } @@ -388,7 +136,7 @@ fn test_dlsym() { assert_eq!(errno, libc::EBADF); } -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", target_os = "illumos")))] fn test_reallocarray() { unsafe { let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); @@ -401,33 +149,94 @@ fn test_reallocarray() { } } -fn main() { - test_posix_gettimeofday(); +fn test_memalign() { + // A normal allocation. + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 8; + let size = 64; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::().write_bytes(1, size); + libc::free(ptr); + } - test_posix_realpath_alloc(); - test_posix_realpath_noalloc(); - test_posix_realpath_errors(); + // Align > size. + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 8; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::().write_bytes(1, size); + libc::free(ptr); + } - test_thread_local_errno(); - test_localtime_r(); + // Size not multiple of align + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 16; + let size = 31; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + assert!(!ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + ptr.cast::().write_bytes(1, size); + libc::free(ptr); + } - test_isatty(); + // Size == 0 + unsafe { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); + // We are not required to return null if size == 0, but we currently do. + // It's fine to remove this assert if we start returning non-null pointers. + assert!(ptr.is_null()); + assert!(ptr.is_aligned_to(align)); + // Regardless of what we return, it must be `free`able. + libc::free(ptr); + } - test_clocks(); + // Non-power of 2 align + unsafe { + let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567); + let align = 15; + let size = 8; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL); + // The pointer is not modified on failure, posix_memalign(3) says: + // > On Linux (and other systems), posix_memalign() does not modify memptr on failure. + // > A requirement standardizing this behavior was added in POSIX.1-2008 TC2. + assert_eq!(ptr.addr(), 0x1234567); + } + + // Too small align (smaller than ptr) + unsafe { + let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567); + let align = std::mem::size_of::() / 2; + let size = 8; + assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL); + // The pointer is not modified on failure, posix_memalign(3) says: + // > On Linux (and other systems), posix_memalign() does not modify memptr on failure. + // > A requirement standardizing this behavior was added in POSIX.1-2008 TC2. + assert_eq!(ptr.addr(), 0x1234567); + } +} + +fn main() { + test_thread_local_errno(); test_dlsym(); test_memcpy(); test_strcpy(); - #[cfg(not(target_os = "macos"))] // reallocarray does not exist on macOS + test_memalign(); + #[cfg(not(any(target_os = "macos", target_os = "illumos")))] test_reallocarray(); - // These are Linux-specific #[cfg(target_os = "linux")] - { - test_posix_fadvise(); - test_sync_file_range(); - test_sigrt(); - } + test_sigrt(); } diff --git a/tests/pass-dep/shims/libc-time.rs b/tests/pass-dep/shims/libc-time.rs new file mode 100644 index 0000000000..69c75bd8ca --- /dev/null +++ b/tests/pass-dep/shims/libc-time.rs @@ -0,0 +1,93 @@ +//@ignore-target-windows: no libc on Windows +//@compile-flags: -Zmiri-disable-isolation +use std::ffi::CStr; +use std::{env, mem, ptr}; + +fn main() { + test_clocks(); + test_posix_gettimeofday(); + test_localtime_r(); +} + +/// 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); + #[cfg(any(target_os = "linux", target_os = "freebsd"))] + { + 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); + } + #[cfg(target_os = "macos")] + { + let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) }; + assert_eq!(is_error, 0); + } +} + +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); + let tv = unsafe { tp.assume_init() }; + assert!(tv.tv_sec > 0); + assert!(tv.tv_usec >= 0); // Theoretically this could be 0. + + // Test that non-null tz returns an error. + let mut tz = mem::MaybeUninit::::uninit(); + let tz_ptr = tz.as_mut_ptr(); + let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; + assert_eq!(is_error, -1); +} + +fn test_localtime_r() { + // Set timezone to GMT. + let key = "TZ"; + env::set_var(key, "GMT"); + + const TIME_SINCE_EPOCH: libc::time_t = 1712475836; + let custom_time_ptr = &TIME_SINCE_EPOCH; + let mut tm = libc::tm { + tm_sec: 0, + tm_min: 0, + tm_hour: 0, + tm_mday: 0, + tm_mon: 0, + tm_year: 0, + tm_wday: 0, + tm_yday: 0, + tm_isdst: 0, + tm_gmtoff: 0, + tm_zone: std::ptr::null_mut::(), + }; + let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) }; + + assert_eq!(tm.tm_sec, 56); + assert_eq!(tm.tm_min, 43); + assert_eq!(tm.tm_hour, 7); + assert_eq!(tm.tm_mday, 7); + assert_eq!(tm.tm_mon, 3); + assert_eq!(tm.tm_year, 124); + assert_eq!(tm.tm_wday, 0); + assert_eq!(tm.tm_yday, 97); + assert_eq!(tm.tm_isdst, -1); + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] + assert_eq!(tm.tm_gmtoff, 0); + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "freebsd"))] + unsafe { + assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") + }; + + // The returned value is the pointer passed in. + assert!(ptr::eq(res, &mut tm)); + + // Remove timezone setting. + env::remove_var(key); +} diff --git a/tests/pass-dep/shims/posix_memalign.rs b/tests/pass-dep/shims/posix_memalign.rs deleted file mode 100644 index db66b21341..0000000000 --- a/tests/pass-dep/shims/posix_memalign.rs +++ /dev/null @@ -1,82 +0,0 @@ -//@ignore-target-windows: No libc on Windows - -#![feature(pointer_is_aligned_to)] -#![feature(strict_provenance)] - -use core::ptr; - -fn main() { - // A normal allocation. - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 8; - let size = 64; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::().write_bytes(1, size); - libc::free(ptr); - } - - // Align > size. - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 64; - let size = 8; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::().write_bytes(1, size); - libc::free(ptr); - } - - // Size not multiple of align - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 16; - let size = 31; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - assert!(!ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - ptr.cast::().write_bytes(1, size); - libc::free(ptr); - } - - // Size == 0 - unsafe { - let mut ptr: *mut libc::c_void = ptr::null_mut(); - let align = 64; - let size = 0; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - // We are not required to return null if size == 0, but we currently do. - // It's fine to remove this assert if we start returning non-null pointers. - assert!(ptr.is_null()); - assert!(ptr.is_aligned_to(align)); - // Regardless of what we return, it must be `free`able. - libc::free(ptr); - } - - // Non-power of 2 align - unsafe { - let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567); - let align = 15; - let size = 8; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL); - // The pointer is not modified on failure, posix_memalign(3) says: - // > On Linux (and other systems), posix_memalign() does not modify memptr on failure. - // > A requirement standardizing this behavior was added in POSIX.1-2008 TC2. - assert_eq!(ptr.addr(), 0x1234567); - } - - // Too small align (smaller than ptr) - unsafe { - let mut ptr: *mut libc::c_void = ptr::without_provenance_mut(0x1234567); - let align = std::mem::size_of::() / 2; - let size = 8; - assert_eq!(libc::posix_memalign(&mut ptr, align, size), libc::EINVAL); - // The pointer is not modified on failure, posix_memalign(3) says: - // > On Linux (and other systems), posix_memalign() does not modify memptr on failure. - // > A requirement standardizing this behavior was added in POSIX.1-2008 TC2. - assert_eq!(ptr.addr(), 0x1234567); - } -} From 038810195064d4e93e62f8cea2c004fbac65d6a3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 14:19:14 +0200 Subject: [PATCH 171/208] move randomness tests into a single file and share the getrandom implementation across unices --- ci/ci.sh | 8 ++-- src/shims/unix/foreign_items.rs | 20 ++++++++- src/shims/unix/freebsd/foreign_items.rs | 28 ++++--------- src/shims/unix/linux/foreign_items.rs | 7 +--- tests/pass-dep/shims/libc-getentropy.rs | 13 ------ .../shims/libc-getrandom-without-isolation.rs | 41 ------------------- .../{libc-getrandom.rs => libc-random.rs} | 26 ++++++++++-- 7 files changed, 55 insertions(+), 88 deletions(-) delete mode 100644 tests/pass-dep/shims/libc-getentropy.rs delete mode 100644 tests/pass-dep/shims/libc-getrandom-without-isolation.rs rename tests/pass-dep/shims/{libc-getrandom.rs => libc-random.rs} (61%) diff --git a/ci/ci.sh b/ci/ci.sh index 6881f3695d..713c97830a 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -143,12 +143,12 @@ case $HOST_TARGET in # Partially supported targets (tier 2) VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus - MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-getentropy libc-getrandom libc-misc fs env num_cpus - MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic - MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus + MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus + MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random # TODO fix solaris stack guard # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync + MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 45793602b2..595cf64a4e 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -23,7 +23,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { // well allow it in `dlsym`. "signal" => true, // needed at least on macOS to avoid file-based fallback in getrandom - "getentropy" => true, + "getentropy" | "getrandom" => true, // Give specific OSes a chance to allow their symbols. _ => match target_os { @@ -632,6 +632,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_i32(0), dest)?; } } + "getrandom" => { + // This function is non-standard but exists with the same signature and behavior on + // Linux, FreeBSD and Solaris/Illumos. + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris") { + throw_unsup_format!( + "`getentropy` is not supported on {}", + this.tcx.sess.target.os + ); + } + let [ptr, len, flags] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + let len = this.read_target_usize(len)?; + let _flags = this.read_scalar(flags)?.to_i32()?; + // We ignore the flags, just always use the same PRNG / host RNG. + this.gen_random(ptr, len)?; + this.write_scalar(Scalar::from_target_usize(len, this), dest)?; + } // 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. diff --git a/src/shims/unix/freebsd/foreign_items.rs b/src/shims/unix/freebsd/foreign_items.rs index 0d5d5cb3d9..e70cd35dda 100644 --- a/src/shims/unix/freebsd/foreign_items.rs +++ b/src/shims/unix/freebsd/foreign_items.rs @@ -20,11 +20,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let this = self.eval_context_mut(); match link_name.as_str() { // Threading - "pthread_attr_get_np" if this.frame_in_std() => { - let [_thread, _attr] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.write_null(dest)?; - } "pthread_set_name_np" => { let [thread, name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; @@ -75,27 +70,20 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Miscellaneous - "getrandom" => { - let [ptr, len, flags] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let ptr = this.read_pointer(ptr)?; - let len = this.read_target_usize(len)?; - let _flags = this.read_scalar(flags)?.to_i32()?; - // flags on freebsd does not really matter - // in practice, GRND_RANDOM does not particularly draw from /dev/random - // since it is the same as to /dev/urandom. - // GRND_INSECURE is only an alias of GRND_NONBLOCK, which - // does not affect the RNG. - // https://man.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2&n=1 - this.gen_random(ptr, len)?; - this.write_scalar(Scalar::from_target_usize(len, this), dest)?; - } "__error" => { let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } + // Incomplete shims that we "stub out" just to get pre-main initialization code to work. + // These shims are enabled only when the caller is in the standard library. + "pthread_attr_get_np" if this.frame_in_std() => { + let [_thread, _attr] = + this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + this.write_null(dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), } Ok(EmulateItemResult::NeedsJumping) diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index 43b5db9e32..ecf82f26a5 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -11,7 +11,7 @@ use shims::unix::linux::mem::EvalContextExt as _; use shims::unix::linux::sync::futex; pub fn is_dyn_sym(name: &str) -> bool { - matches!(name, "getrandom" | "statx") + matches!(name, "statx") } impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} @@ -140,11 +140,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // Miscellaneous - "getrandom" => { - let [ptr, len, flags] = - this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - getrandom(this, ptr, len, flags, dest)?; - } "mmap64" => { let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/tests/pass-dep/shims/libc-getentropy.rs b/tests/pass-dep/shims/libc-getentropy.rs deleted file mode 100644 index 06109397c2..0000000000 --- a/tests/pass-dep/shims/libc-getentropy.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ignore-target-windows: no libc - -use libc::getentropy; - -fn main() { - let mut buf1 = [0u8; 256]; - let mut buf2 = [0u8; 257]; - unsafe { - assert_eq!(getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0); - assert_eq!(getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1); - assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::EIO); - } -} diff --git a/tests/pass-dep/shims/libc-getrandom-without-isolation.rs b/tests/pass-dep/shims/libc-getrandom-without-isolation.rs deleted file mode 100644 index 349b447569..0000000000 --- a/tests/pass-dep/shims/libc-getrandom-without-isolation.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@only-target-linux -//@compile-flags: -Zmiri-disable-isolation - -use std::ptr; - -fn main() { - let mut buf = [0u8; 5]; - unsafe { - assert_eq!( - libc::syscall( - libc::SYS_getrandom, - ptr::null_mut::(), - 0 as libc::size_t, - 0 as libc::c_uint, - ), - 0, - ); - assert_eq!( - libc::syscall( - libc::SYS_getrandom, - buf.as_mut_ptr() as *mut libc::c_void, - 5 as libc::size_t, - 0 as libc::c_uint, - ), - 5, - ); - - assert_eq!( - libc::getrandom(ptr::null_mut::(), 0 as libc::size_t, 0 as libc::c_uint), - 0, - ); - assert_eq!( - libc::getrandom( - buf.as_mut_ptr() as *mut libc::c_void, - 5 as libc::size_t, - 0 as libc::c_uint, - ), - 5, - ); - } -} diff --git a/tests/pass-dep/shims/libc-getrandom.rs b/tests/pass-dep/shims/libc-random.rs similarity index 61% rename from tests/pass-dep/shims/libc-getrandom.rs rename to tests/pass-dep/shims/libc-random.rs index 9c670cbd50..71e3352263 100644 --- a/tests/pass-dep/shims/libc-getrandom.rs +++ b/tests/pass-dep/shims/libc-random.rs @@ -1,9 +1,29 @@ //@ignore-target-windows: no libc -//@ignore-target-apple: no getrandom - -use std::ptr; +//@revisions: isolation no_isolation +//@[no_isolation]compile-flags: -Zmiri-disable-isolation fn main() { + test_getentropy(); + #[cfg(not(target_os = "macos"))] + test_getrandom(); +} + +fn test_getentropy() { + use libc::getentropy; + + let mut buf1 = [0u8; 256]; + let mut buf2 = [0u8; 257]; + unsafe { + assert_eq!(getentropy(buf1.as_mut_ptr() as *mut libc::c_void, buf1.len()), 0); + assert_eq!(getentropy(buf2.as_mut_ptr() as *mut libc::c_void, buf2.len()), -1); + assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::EIO); + } +} + +#[cfg(not(target_os = "macos"))] +fn test_getrandom() { + use std::ptr; + let mut buf = [0u8; 5]; unsafe { #[cfg(target_os = "linux")] From 3baa98413e93dbca62f0a3965d8733ac7bb7b78e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 14:41:12 +0200 Subject: [PATCH 172/208] hello: also ensure stderr works --- tests/pass/hello.rs | 1 + tests/pass/hello.stderr | 1 + 2 files changed, 2 insertions(+) create mode 100644 tests/pass/hello.stderr diff --git a/tests/pass/hello.rs b/tests/pass/hello.rs index e7a11a969c..2a02a7a9e2 100644 --- a/tests/pass/hello.rs +++ b/tests/pass/hello.rs @@ -1,3 +1,4 @@ fn main() { println!("Hello, world!"); + eprintln!("Hello, error!"); } diff --git a/tests/pass/hello.stderr b/tests/pass/hello.stderr new file mode 100644 index 0000000000..445a1bc8fa --- /dev/null +++ b/tests/pass/hello.stderr @@ -0,0 +1 @@ +Hello, error! From 279dd350b9cc54cad5d69332ade13808b9acc4a7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 18:47:37 +0200 Subject: [PATCH 173/208] unix/fs: a bit of cleanup around host-specific code --- src/shims/unix/fs.rs | 46 +++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 058747916c..57ae209416 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -137,37 +137,28 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx &mut self, file_type: std::io::Result, ) -> InterpResult<'tcx, i32> { + #[cfg(unix)] + use std::os::unix::fs::FileTypeExt; + let this = self.eval_context_mut(); match file_type { Ok(file_type) => { - if file_type.is_dir() { - Ok(this.eval_libc("DT_DIR").to_u8()?.into()) - } else if file_type.is_file() { - Ok(this.eval_libc("DT_REG").to_u8()?.into()) - } else if file_type.is_symlink() { - Ok(this.eval_libc("DT_LNK").to_u8()?.into()) - } else { + match () { + _ if file_type.is_dir() => Ok(this.eval_libc("DT_DIR").to_u8()?.into()), + _ if file_type.is_file() => Ok(this.eval_libc("DT_REG").to_u8()?.into()), + _ if file_type.is_symlink() => Ok(this.eval_libc("DT_LNK").to_u8()?.into()), // Certain file types are only supported when the host is a Unix system. - // (i.e. devices and sockets) If it is, check those cases, if not, fall back to - // DT_UNKNOWN sooner. - #[cfg(unix)] - { - use std::os::unix::fs::FileTypeExt; - if file_type.is_block_device() { - Ok(this.eval_libc("DT_BLK").to_u8()?.into()) - } else if file_type.is_char_device() { - Ok(this.eval_libc("DT_CHR").to_u8()?.into()) - } else if file_type.is_fifo() { - Ok(this.eval_libc("DT_FIFO").to_u8()?.into()) - } else if file_type.is_socket() { - Ok(this.eval_libc("DT_SOCK").to_u8()?.into()) - } else { - Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()) - } - } - #[cfg(not(unix))] - Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()) + _ if file_type.is_block_device() => + Ok(this.eval_libc("DT_BLK").to_u8()?.into()), + #[cfg(unix)] + _ if file_type.is_char_device() => Ok(this.eval_libc("DT_CHR").to_u8()?.into()), + #[cfg(unix)] + _ if file_type.is_fifo() => Ok(this.eval_libc("DT_FIFO").to_u8()?.into()), + #[cfg(unix)] + _ if file_type.is_socket() => Ok(this.eval_libc("DT_SOCK").to_u8()?.into()), + // Fallback + _ => Ok(this.eval_libc("DT_UNKNOWN").to_u8()?.into()), } } Err(e) => @@ -1314,7 +1305,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } - #[cfg_attr(not(unix), allow(unused))] fn isatty( &mut self, miri_fd: &OpTy<'tcx, Provenance>, @@ -1467,8 +1457,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { #[cfg(unix)] { use std::os::unix::fs::OpenOptionsExt; - fopts.mode(0o600); // Do not allow others to read or modify this file. + fopts.mode(0o600); fopts.custom_flags(libc::O_EXCL); } #[cfg(windows)] From f47926c6f652dabfa1581ed23177375f42a50c82 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 May 2024 17:06:42 +0100 Subject: [PATCH 174/208] solaris support start. --- README.md | 2 +- ci/ci.sh | 3 +-- src/shims/unix/mem.rs | 7 +++++-- src/shims/unix/solarish/foreign_items.rs | 19 ++++++++++++++++++- tests/pass-dep/shims/libc-misc.rs | 4 ++-- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5b3e2a588b..d086987f43 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ degree documented below): - We have unofficial support (not maintained by the Miri team itself) for some further operating systems. - `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - - `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works. + - `solaris` / `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works. - `wasm`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. diff --git a/ci/ci.sh b/ci/ci.sh index 713c97830a..d1dcacdfdf 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -146,8 +146,7 @@ case $HOST_TARGET in MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random - # TODO fix solaris stack guard - # MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic pthread-sync + MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm diff --git a/src/shims/unix/mem.rs b/src/shims/unix/mem.rs index f52dc23656..0254735ac1 100644 --- a/src/shims/unix/mem.rs +++ b/src/shims/unix/mem.rs @@ -42,9 +42,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let map_shared = this.eval_libc_i32("MAP_SHARED"); let map_fixed = this.eval_libc_i32("MAP_FIXED"); - // This is a horrible hack, but on MacOS the guard page mechanism uses mmap + // This is a horrible hack, but on MacOS and Solaris the guard page mechanism uses mmap // in a way we do not support. We just give it the return value it expects. - if this.frame_in_std() && this.tcx.sess.target.os == "macos" && (flags & map_fixed) != 0 { + if this.frame_in_std() + && matches!(&*this.tcx.sess.target.os, "macos" | "solaris") + && (flags & map_fixed) != 0 + { return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this)); } diff --git a/src/shims/unix/solarish/foreign_items.rs b/src/shims/unix/solarish/foreign_items.rs index c01ae0ecb9..c216d8afd7 100644 --- a/src/shims/unix/solarish/foreign_items.rs +++ b/src/shims/unix/solarish/foreign_items.rs @@ -2,7 +2,6 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use crate::*; -use shims::EmulateItemResult; pub fn is_dyn_sym(_name: &str) -> bool { false @@ -26,6 +25,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } + "stack_getbounds" => { + let [stack] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?; + + this.write_int_fields_named( + &[ + ("ss_sp", this.machine.stack_addr.into()), + ("ss_size", this.machine.stack_size.into()), + // field set to 0 means not in an alternate signal stack + // https://docs.oracle.com/cd/E86824_01/html/E54766/stack-getbounds-3c.html + ("ss_flags", 0), + ], + &stack, + )?; + + this.write_null(dest)?; + } + _ => return Ok(EmulateItemResult::NotSupported), } Ok(EmulateItemResult::NeedsJumping) diff --git a/tests/pass-dep/shims/libc-misc.rs b/tests/pass-dep/shims/libc-misc.rs index 9644920c49..32898d4959 100644 --- a/tests/pass-dep/shims/libc-misc.rs +++ b/tests/pass-dep/shims/libc-misc.rs @@ -136,7 +136,7 @@ fn test_dlsym() { assert_eq!(errno, libc::EBADF); } -#[cfg(not(any(target_os = "macos", target_os = "illumos")))] +#[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] fn test_reallocarray() { unsafe { let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); @@ -234,7 +234,7 @@ fn main() { test_strcpy(); test_memalign(); - #[cfg(not(any(target_os = "macos", target_os = "illumos")))] + #[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] test_reallocarray(); #[cfg(target_os = "linux")] From 8442231758a423772a46773506ab5a5cf804f3d4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 22:34:53 +0200 Subject: [PATCH 175/208] reduce tokio features --- test_dependencies/Cargo.lock | 78 ------------------------------------ test_dependencies/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 79 deletions(-) diff --git a/test_dependencies/Cargo.lock b/test_dependencies/Cargo.lock index 3dd5eb9b91..e11159f9e6 100644 --- a/test_dependencies/Cargo.lock +++ b/test_dependencies/Cargo.lock @@ -17,12 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - [[package]] name = "backtrace" version = "0.3.71" @@ -50,12 +44,6 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "bytes" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" - [[package]] name = "cc" version = "1.0.96" @@ -141,16 +129,6 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -233,29 +211,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "parking_lot" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.5", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -316,15 +271,6 @@ dependencies = [ "getrandom 0.2.14", ] -[[package]] -name = "redox_syscall" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -344,27 +290,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "socket2" version = "0.5.7" @@ -405,13 +330,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", - "bytes", "libc", "mio", "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", diff --git a/test_dependencies/Cargo.toml b/test_dependencies/Cargo.toml index 9f28e4d169..f693aab242 100644 --- a/test_dependencies/Cargo.toml +++ b/test_dependencies/Cargo.toml @@ -19,7 +19,7 @@ rand = { version = "0.8", features = ["small_rng"] } [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies] page_size = "0.6" -tokio = { version = "1.24", features = ["full"] } +tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net"] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52", features = [ "Win32_Foundation", "Win32_System_Threading" ] } From f2ea8cd691dd5198ea9c3cdc8267e9c80b3e9ff4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 22:36:00 +0200 Subject: [PATCH 176/208] remove rand test the actual target-specific things we want to test are all in getrandom, and rand already tests miri itself --- test_dependencies/Cargo.lock | 37 ------------------------------------ test_dependencies/Cargo.toml | 1 - tests/pass-dep/rand.rs | 23 ---------------------- 3 files changed, 61 deletions(-) delete mode 100644 tests/pass-dep/rand.rs diff --git a/test_dependencies/Cargo.lock b/test_dependencies/Cargo.lock index e11159f9e6..c73d13a462 100644 --- a/test_dependencies/Cargo.lock +++ b/test_dependencies/Cargo.lock @@ -170,7 +170,6 @@ dependencies = [ "libc", "num_cpus", "page_size", - "rand", "tempfile", "tokio", "windows-sys 0.52.0", @@ -217,12 +216,6 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - [[package]] name = "proc-macro2" version = "1.0.81" @@ -241,36 +234,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.14", -] - [[package]] name = "rustc-demangle" version = "0.1.23" diff --git a/test_dependencies/Cargo.toml b/test_dependencies/Cargo.toml index f693aab242..1894f53ce4 100644 --- a/test_dependencies/Cargo.toml +++ b/test_dependencies/Cargo.toml @@ -15,7 +15,6 @@ tempfile = "3" getrandom_01 = { package = "getrandom", version = "0.1" } getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] } -rand = { version = "0.8", features = ["small_rng"] } [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies] page_size = "0.6" diff --git a/tests/pass-dep/rand.rs b/tests/pass-dep/rand.rs deleted file mode 100644 index 0dce6d86cf..0000000000 --- a/tests/pass-dep/rand.rs +++ /dev/null @@ -1,23 +0,0 @@ -//@compile-flags: -Zmiri-strict-provenance -use rand::prelude::*; - -// Test using the `rand` crate to generate randomness. -fn main() { - // Fully deterministic seeding. - let mut rng = SmallRng::seed_from_u64(42); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); - - // Try seeding with "real" entropy. - let mut rng = SmallRng::from_entropy(); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); - - // Also try per-thread RNG. - let mut rng = rand::thread_rng(); - let _val = rng.gen::(); - let _val = rng.gen::(); - let _val = rng.gen::(); -} From 1ae97809bd30d347f9b976254f49e6000330d69c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 5 May 2024 22:38:19 +0200 Subject: [PATCH 177/208] getrandom: test with and without isolation also add some comments for why we keep certain old obscure APIs supported --- src/shims/unix/linux/foreign_items.rs | 33 +++++++++------------------ src/shims/windows/foreign_items.rs | 3 +++ tests/pass-dep/getrandom.rs | 3 ++- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/shims/unix/linux/foreign_items.rs b/src/shims/unix/linux/foreign_items.rs index ecf82f26a5..7cd749a410 100644 --- a/src/shims/unix/linux/foreign_items.rs +++ b/src/shims/unix/linux/foreign_items.rs @@ -117,6 +117,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)` // is called if a `HashMap` is created the regular way (e.g. HashMap). id if id == sys_getrandom => { + // Used by getrandom 0.1 // The first argument is the syscall id, so skip over it. if args.len() < 4 { throw_ub_format!( @@ -124,7 +125,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { args.len() ); } - getrandom(this, &args[1], &args[2], &args[3], dest)?; + + let ptr = this.read_pointer(&args[1])?; + let len = this.read_target_usize(&args[2])?; + // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, + // neither of which have any effect on our current PRNG. + // See for a discussion of argument sizes. + let _flags = this.read_scalar(&args[3])?.to_i32(); + + this.gen_random(ptr, len)?; + this.write_scalar(Scalar::from_target_usize(len, this), dest)?; } // `futex` is used by some synchronization primitives. id if id == sys_futex => { @@ -196,24 +206,3 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(EmulateItemResult::NeedsJumping) } } - -// Shims the linux `getrandom` syscall. -fn getrandom<'tcx>( - this: &mut MiriInterpCx<'_, 'tcx>, - ptr: &OpTy<'tcx, Provenance>, - len: &OpTy<'tcx, Provenance>, - flags: &OpTy<'tcx, Provenance>, - dest: &MPlaceTy<'tcx, Provenance>, -) -> InterpResult<'tcx> { - let ptr = this.read_pointer(ptr)?; - let len = this.read_target_usize(len)?; - - // The only supported flags are GRND_RANDOM and GRND_NONBLOCK, - // neither of which have any effect on our current PRNG. - // See for a discussion of argument sizes. - let _flags = this.read_scalar(flags)?.to_i32(); - - this.gen_random(ptr, len)?; - this.write_scalar(Scalar::from_target_usize(len, this), dest)?; - Ok(()) -} diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index dba5b7a906..28dad97477 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -513,6 +513,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false }); } "SystemFunction036" => { + // used by getrandom 0.1 // This is really 'RtlGenRandom'. let [ptr, len] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; @@ -522,6 +523,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_bool(true), dest)?; } "ProcessPrng" => { + // used by `std` let [ptr, len] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; @@ -530,6 +532,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_i32(1), dest)?; } "BCryptGenRandom" => { + // used by getrandom 0.2 let [algorithm, ptr, len, flags] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let algorithm = this.read_scalar(algorithm)?; diff --git a/tests/pass-dep/getrandom.rs b/tests/pass-dep/getrandom.rs index c0d9296a9a..53de3af763 100644 --- a/tests/pass-dep/getrandom.rs +++ b/tests/pass-dep/getrandom.rs @@ -1,8 +1,9 @@ // mac-os `getrandom_01` does some pointer shenanigans //@compile-flags: -Zmiri-permissive-provenance +//@revisions: isolation no_isolation +//@[no_isolation]compile-flags: -Zmiri-disable-isolation /// Test direct calls of getrandom 0.1 and 0.2. -/// Make sure they work even with isolation enabled (i.e., we do not hit a file-based fallback path). fn main() { let mut data = vec![0; 16]; getrandom_01::getrandom(&mut data).unwrap(); From 381879460208238c98a386360566aac5adad0ba3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 May 2024 09:45:11 +0200 Subject: [PATCH 178/208] avoid code duplication between realloc and malloc --- src/shims/alloc.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/shims/alloc.rs b/src/shims/alloc.rs index 79531598c0..61e639f76e 100644 --- a/src/shims/alloc.rs +++ b/src/shims/alloc.rs @@ -124,13 +124,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let new_align = this.min_align(new_size, kind); if this.ptr_is_null(old_ptr)? { // Here we must behave like `malloc`. - if new_size == 0 { - Ok(Pointer::null()) - } else { - let new_ptr = - this.allocate_ptr(Size::from_bytes(new_size), new_align, kind.into())?; - Ok(new_ptr.into()) - } + self.malloc(new_size, /*zero_init*/ false, kind) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. From 4de677f18d8e410579083fb3bc090e8323879657 Mon Sep 17 00:00:00 2001 From: Asger Hautop Drewsen Date: Tue, 16 Apr 2024 13:33:08 +0200 Subject: [PATCH 179/208] Implement wcslen --- src/helpers.rs | 43 ++++++++++++++++++++++++++++++-------- src/shims/foreign_items.rs | 10 +++++++++ tests/pass-dep/wcslen.rs | 20 ++++++++++++++++++ 3 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 tests/pass-dep/wcslen.rs diff --git a/src/helpers.rs b/src/helpers.rs index 40c2008ac9..795f21ddb9 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -982,29 +982,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((true, string_length)) } - /// Read a sequence of u16 until the first null terminator. - fn read_wide_str(&self, mut ptr: Pointer>) -> InterpResult<'tcx, Vec> { + /// Helper function to read a sequence of unsigned integers of the given size and alignment + /// until the first null terminator. + fn read_c_str_with_char_size( + &self, + mut ptr: Pointer>, + size: Size, + align: Align, + ) -> InterpResult<'tcx, Vec> + where + T: TryFrom, + >::Error: std::fmt::Debug, + { + assert_ne!(size, Size::ZERO); + let this = self.eval_context_ref(); - let size2 = Size::from_bytes(2); - this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?; + + this.check_ptr_align(ptr, align)?; let mut wchars = Vec::new(); loop { // FIXME: We are re-getting the allocation each time around the loop. // Would be nice if we could somehow "extend" an existing AllocRange. - let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result - let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?; - if wchar == 0 { + let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); // not a ZST, so we will get a result + let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?; + if wchar_int == 0 { break; } else { - wchars.push(wchar); - ptr = ptr.offset(size2, this)?; + wchars.push(wchar_int.try_into().unwrap()); + ptr = ptr.offset(size, this)?; } } Ok(wchars) } + /// Read a sequence of u16 until the first null terminator. + fn read_wide_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap()) + } + /// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying /// to write if `size` is not large enough to fit the contents of `os_string` plus a null @@ -1037,6 +1054,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok((true, string_length)) } + /// Read a sequence of wchar_t until the first null terminator. + /// Always returns a `Vec` no matter the size of `wchar_t`. + fn read_wchar_t_str(&self, ptr: Pointer>) -> InterpResult<'tcx, Vec> { + let this = self.eval_context_ref(); + let wchar_t = this.libc_ty_layout("wchar_t"); + self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) + } + /// Check that the ABI is what we expect. fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> { if abi != exp_abi { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index c51a27b745..28028479ac 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -657,6 +657,16 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { dest, )?; } + "wcslen" => { + let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + let ptr = this.read_pointer(ptr)?; + // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. + let n = this.read_wchar_t_str(ptr)?.len(); + this.write_scalar( + Scalar::from_target_usize(u64::try_from(n).unwrap(), this), + dest, + )?; + } "memcpy" => { let [ptr_dest, ptr_src, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; diff --git a/tests/pass-dep/wcslen.rs b/tests/pass-dep/wcslen.rs new file mode 100644 index 0000000000..c5c9d99247 --- /dev/null +++ b/tests/pass-dep/wcslen.rs @@ -0,0 +1,20 @@ +fn to_c_wchar_t_str(s: &str) -> Vec { + let mut r = Vec::::new(); + for c in s.bytes() { + if c == 0 { + panic!("can't contain a null character"); + } + if c >= 128 { + panic!("only ASCII supported"); + } + r.push(c.into()); + } + r.push(0); + r +} + +pub fn main() { + let s = to_c_wchar_t_str("Rust"); + let len = unsafe { libc::wcslen(s.as_ptr()) }; + assert_eq!(len, 4); +} From 325af11a21e5c771bf32feb653be6399c734286a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 6 May 2024 11:15:07 +0200 Subject: [PATCH 180/208] organize libc tests into a proper folder, and run some of them on Windows --- ci/ci.sh | 8 +- .../libc_pthread_cond_double_destroy.rs | 2 +- .../libc_pthread_cond_double_destroy.stderr | 0 .../libc_pthread_condattr_double_destroy.rs | 2 +- ...ibc_pthread_condattr_double_destroy.stderr | 0 .../libc_pthread_create_main_terminate.rs | 2 +- .../libc_pthread_create_too_few_args.rs | 2 +- .../libc_pthread_create_too_many_args.rs | 2 +- .../concurrency/libc_pthread_join_detached.rs | 2 +- .../concurrency/libc_pthread_join_joined.rs | 2 +- .../concurrency/libc_pthread_join_main.rs | 2 +- .../concurrency/libc_pthread_join_multiple.rs | 2 +- .../concurrency/libc_pthread_join_self.rs | 2 +- .../libc_pthread_mutex_NULL_deadlock.rs | 2 +- .../libc_pthread_mutex_NULL_deadlock.stderr | 0 .../libc_pthread_mutex_deadlock.rs | 2 +- .../libc_pthread_mutex_deadlock.stderr | 0 .../libc_pthread_mutex_default_deadlock.rs | 2 +- ...libc_pthread_mutex_default_deadlock.stderr | 0 .../libc_pthread_mutex_destroy_locked.rs | 2 +- .../libc_pthread_mutex_destroy_locked.stderr | 0 .../libc_pthread_mutex_double_destroy.rs | 2 +- .../libc_pthread_mutex_double_destroy.stderr | 0 .../libc_pthread_mutex_normal_deadlock.rs | 2 +- .../libc_pthread_mutex_normal_deadlock.stderr | 0 ...bc_pthread_mutex_normal_unlock_unlocked.rs | 2 +- ...thread_mutex_normal_unlock_unlocked.stderr | 0 .../libc_pthread_mutex_wrong_owner.rs | 2 +- .../libc_pthread_mutex_wrong_owner.stderr | 0 .../libc_pthread_mutexattr_double_destroy.rs | 2 +- ...bc_pthread_mutexattr_double_destroy.stderr | 0 ...libc_pthread_rwlock_destroy_read_locked.rs | 2 +- ..._pthread_rwlock_destroy_read_locked.stderr | 0 ...ibc_pthread_rwlock_destroy_write_locked.rs | 2 +- ...pthread_rwlock_destroy_write_locked.stderr | 0 .../libc_pthread_rwlock_double_destroy.rs | 2 +- .../libc_pthread_rwlock_double_destroy.stderr | 0 ...wlock_read_write_deadlock_single_thread.rs | 2 +- ...k_read_write_deadlock_single_thread.stderr | 0 .../libc_pthread_rwlock_read_wrong_owner.rs | 2 +- ...ibc_pthread_rwlock_read_wrong_owner.stderr | 0 .../libc_pthread_rwlock_unlock_unlocked.rs | 2 +- ...libc_pthread_rwlock_unlock_unlocked.stderr | 0 ...libc_pthread_rwlock_write_read_deadlock.rs | 2 +- ..._pthread_rwlock_write_read_deadlock.stderr | 0 ...wlock_write_read_deadlock_single_thread.rs | 2 +- ...k_write_read_deadlock_single_thread.stderr | 0 ...ibc_pthread_rwlock_write_write_deadlock.rs | 2 +- ...pthread_rwlock_write_write_deadlock.stderr | 0 ...lock_write_write_deadlock_single_thread.rs | 2 +- ..._write_write_deadlock_single_thread.stderr | 0 .../libc_pthread_rwlock_write_wrong_owner.rs | 2 +- ...bc_pthread_rwlock_write_wrong_owner.stderr | 0 .../{shims => libc}/env-set_var-data-race.rs | 2 +- .../env-set_var-data-race.stderr | 0 .../{shims => libc}/fs/close_stdout.rs | 2 +- .../{shims => libc}/fs/close_stdout.stderr | 0 .../{shims => libc}/fs/isolated_stdin.rs | 2 +- .../{shims => libc}/fs/isolated_stdin.stderr | 0 .../fs/mkstemp_immutable_arg.rs | 2 +- .../fs/mkstemp_immutable_arg.stderr | 0 .../{shims => libc}/fs/read_from_stdout.rs | 2 +- .../fs/read_from_stdout.stderr | 0 .../fs/unix_open_missing_required_mode.rs | 2 +- .../fs/unix_open_missing_required_mode.stderr | 0 .../{shims => libc}/fs/write_to_stdin.rs | 2 +- .../{shims => libc}/fs/write_to_stdin.stderr | 0 tests/fail-dep/{shims => libc}/memchr_null.rs | 2 - .../{shims => libc}/memchr_null.stderr | 0 tests/fail-dep/{shims => libc}/memcmp_null.rs | 2 - .../{shims => libc}/memcmp_null.stderr | 0 tests/fail-dep/{shims => libc}/memcmp_zero.rs | 1 - .../{shims => libc}/memcmp_zero.stderr | 0 tests/fail-dep/{shims => libc}/memcpy_zero.rs | 1 - .../{shims => libc}/memcpy_zero.stderr | 0 .../fail-dep/{shims => libc}/memrchr_null.rs | 2 +- .../{shims => libc}/memrchr_null.stderr | 0 .../{shims => libc}/mmap_invalid_dealloc.rs | 2 +- .../mmap_invalid_dealloc.stderr | 0 .../{shims => libc}/mmap_use_after_munmap.rs | 2 +- .../mmap_use_after_munmap.stderr | 0 .../{shims => libc}/munmap_partial.rs | 2 +- .../{shims => libc}/munmap_partial.stderr | 0 tests/fail-dep/{ => libc}/realloc-zero.rs | 2 - tests/fail-dep/{ => libc}/realloc-zero.stderr | 0 .../unsupported_incomplete_function.rs | 2 +- .../unsupported_incomplete_function.stderr | 0 tests/panic/unsupported_syscall.rs | 2 +- tests/pass-dep/calloc.rs | 22 --- .../env-cleanup-data-race.rs | 2 +- .../libc_pthread_cond_timedwait.rs | 2 +- .../libc_pthread_cond_timedwait_isolated.rs | 2 +- .../concurrency/tls_pthread_drop_order.rs | 2 +- tests/pass-dep/extra_fn_ptr_gc.rs | 2 +- .../fcntl_f-fullfsync_apple.rs | 0 .../fcntl_f-fullfsync_apple.stderr | 0 .../{shims => libc}/libc-fs-with-isolation.rs | 0 .../libc-fs-with-isolation.stderr | 0 tests/pass-dep/{shims => libc}/libc-fs.rs | 0 tests/pass-dep/{shims => libc}/libc-fs.stderr | 0 tests/pass-dep/{shims => libc}/libc-fs.stdout | 0 .../{shims/libc-misc.rs => libc/libc-mem.rs} | 163 ++++++++++-------- tests/pass-dep/libc/libc-misc.rs | 68 ++++++++ tests/pass-dep/{shims => libc}/libc-random.rs | 0 tests/pass-dep/{shims => libc}/libc-time.rs | 0 tests/pass-dep/{shims => libc}/mmap.rs | 2 +- .../pass-dep/{shims => libc}/pthread-sync.rs | 2 +- .../{shims => libc}/pthread-threadname.rs | 2 +- tests/pass-dep/malloc.rs | 48 ------ 109 files changed, 211 insertions(+), 208 deletions(-) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_cond_double_destroy.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_cond_double_destroy.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_condattr_double_destroy.rs (92%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_condattr_double_destroy.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_NULL_deadlock.rs (90%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_NULL_deadlock.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_deadlock.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_deadlock.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_default_deadlock.rs (91%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_default_deadlock.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_destroy_locked.rs (92%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_destroy_locked.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_double_destroy.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_double_destroy.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_deadlock.rs (92%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_deadlock.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_unlock_unlocked.rs (92%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_normal_unlock_unlocked.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_wrong_owner.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutex_wrong_owner.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutexattr_double_destroy.rs (91%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_mutexattr_double_destroy.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_read_locked.rs (83%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_read_locked.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_write_locked.rs (83%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_destroy_write_locked.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_double_destroy.rs (89%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_double_destroy.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_write_deadlock_single_thread.rs (82%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_wrong_owner.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_read_wrong_owner.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_unlock_unlocked.rs (78%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_unlock_unlocked.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock_single_thread.rs (82%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock_single_thread.rs (82%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr (100%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_wrong_owner.rs (93%) rename tests/fail-dep/{shims/sync => concurrency}/libc_pthread_rwlock_write_wrong_owner.stderr (100%) rename tests/fail-dep/{shims => libc}/env-set_var-data-race.rs (90%) rename tests/fail-dep/{shims => libc}/env-set_var-data-race.stderr (100%) rename tests/fail-dep/{shims => libc}/fs/close_stdout.rs (82%) rename tests/fail-dep/{shims => libc}/fs/close_stdout.stderr (100%) rename tests/fail-dep/{shims => libc}/fs/isolated_stdin.rs (83%) rename tests/fail-dep/{shims => libc}/fs/isolated_stdin.stderr (100%) rename tests/fail-dep/{shims => libc}/fs/mkstemp_immutable_arg.rs (86%) rename tests/fail-dep/{shims => libc}/fs/mkstemp_immutable_arg.stderr (100%) rename tests/fail-dep/{shims => libc}/fs/read_from_stdout.rs (83%) rename tests/fail-dep/{shims => libc}/fs/read_from_stdout.stderr (100%) rename tests/fail-dep/{shims => libc}/fs/unix_open_missing_required_mode.rs (89%) rename tests/fail-dep/{shims => libc}/fs/unix_open_missing_required_mode.stderr (100%) rename tests/fail-dep/{shims => libc}/fs/write_to_stdin.rs (80%) rename tests/fail-dep/{shims => libc}/fs/write_to_stdin.stderr (100%) rename tests/fail-dep/{shims => libc}/memchr_null.rs (77%) rename tests/fail-dep/{shims => libc}/memchr_null.stderr (100%) rename tests/fail-dep/{shims => libc}/memcmp_null.rs (78%) rename tests/fail-dep/{shims => libc}/memcmp_null.stderr (100%) rename tests/fail-dep/{shims => libc}/memcmp_zero.rs (90%) rename tests/fail-dep/{shims => libc}/memcmp_zero.stderr (100%) rename tests/fail-dep/{shims => libc}/memcpy_zero.rs (88%) rename tests/fail-dep/{shims => libc}/memcpy_zero.stderr (100%) rename tests/fail-dep/{shims => libc}/memrchr_null.rs (81%) rename tests/fail-dep/{shims => libc}/memrchr_null.stderr (100%) rename tests/fail-dep/{shims => libc}/mmap_invalid_dealloc.rs (90%) rename tests/fail-dep/{shims => libc}/mmap_invalid_dealloc.stderr (100%) rename tests/fail-dep/{shims => libc}/mmap_use_after_munmap.rs (90%) rename tests/fail-dep/{shims => libc}/mmap_use_after_munmap.stderr (100%) rename tests/fail-dep/{shims => libc}/munmap_partial.rs (94%) rename tests/fail-dep/{shims => libc}/munmap_partial.stderr (100%) rename tests/fail-dep/{ => libc}/realloc-zero.rs (81%) rename tests/fail-dep/{ => libc}/realloc-zero.stderr (100%) rename tests/fail-dep/{ => libc}/unsupported_incomplete_function.rs (87%) rename tests/fail-dep/{ => libc}/unsupported_incomplete_function.stderr (100%) delete mode 100644 tests/pass-dep/calloc.rs rename tests/pass-dep/{shims => concurrency}/env-cleanup-data-race.rs (93%) rename tests/pass-dep/{shims => libc}/fcntl_f-fullfsync_apple.rs (100%) rename tests/pass-dep/{shims => libc}/fcntl_f-fullfsync_apple.stderr (100%) rename tests/pass-dep/{shims => libc}/libc-fs-with-isolation.rs (100%) rename tests/pass-dep/{shims => libc}/libc-fs-with-isolation.stderr (100%) rename tests/pass-dep/{shims => libc}/libc-fs.rs (100%) rename tests/pass-dep/{shims => libc}/libc-fs.stderr (100%) rename tests/pass-dep/{shims => libc}/libc-fs.stdout (100%) rename tests/pass-dep/{shims/libc-misc.rs => libc/libc-mem.rs} (72%) create mode 100644 tests/pass-dep/libc/libc-misc.rs rename tests/pass-dep/{shims => libc}/libc-random.rs (100%) rename tests/pass-dep/{shims => libc}/libc-time.rs (100%) rename tests/pass-dep/{shims => libc}/mmap.rs (99%) rename tests/pass-dep/{shims => libc}/pthread-sync.rs (99%) rename tests/pass-dep/{shims => libc}/pthread-threadname.rs (97%) delete mode 100644 tests/pass-dep/malloc.rs diff --git a/ci/ci.sh b/ci/ci.sh index d1dcacdfdf..c28a6f183b 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -143,10 +143,10 @@ case $HOST_TARGET in # Partially supported targets (tier 2) VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus - MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-misc libc-random libc-time fs env num_cpus - MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random - MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-misc libc-random + MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus + MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus + MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm diff --git a/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs b/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs rename to tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs index 94ca3496ed..f22f17be0d 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.rs +++ b/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_cond twice fails, even without a check for number validity diff --git a/tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.stderr b/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_cond_double_destroy.stderr rename to tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs b/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs similarity index 92% rename from tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs rename to tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs index b5427d55eb..d0ccab4de5 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.rs +++ b/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@ignore-target-apple: Our macOS condattr don't have any fields so we do not notice this. /// Test that destroying a pthread_condattr twice fails, even without a check for number validity diff --git a/tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.stderr b/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_condattr_double_destroy.stderr rename to tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr diff --git a/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs b/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs index 7e6f490bb3..9cd0a35d36 100644 --- a/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs +++ b/tests/fail-dep/concurrency/libc_pthread_create_main_terminate.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: the main thread terminated without waiting for all remaining threads // Check that we terminate the program when the main thread terminates. diff --git a/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs b/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs index e1d3704af7..96dd99e884 100644 --- a/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs +++ b/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //! The thread function must have exactly one argument. diff --git a/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs b/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs index 7408634db5..d8fbc68d34 100644 --- a/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs +++ b/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //! The thread function must have exactly one argument. diff --git a/tests/fail-dep/concurrency/libc_pthread_join_detached.rs b/tests/fail-dep/concurrency/libc_pthread_join_detached.rs index 0b810dc8c7..e89d7a9f02 100644 --- a/tests/fail-dep/concurrency/libc_pthread_join_detached.rs +++ b/tests/fail-dep/concurrency/libc_pthread_join_detached.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining a detached thread is undefined behavior. diff --git a/tests/fail-dep/concurrency/libc_pthread_join_joined.rs b/tests/fail-dep/concurrency/libc_pthread_join_joined.rs index 04ca4bbb3f..cbad743ca5 100644 --- a/tests/fail-dep/concurrency/libc_pthread_join_joined.rs +++ b/tests/fail-dep/concurrency/libc_pthread_join_joined.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining an already joined thread is undefined behavior. diff --git a/tests/fail-dep/concurrency/libc_pthread_join_main.rs b/tests/fail-dep/concurrency/libc_pthread_join_main.rs index 7576518216..002498e6c8 100644 --- a/tests/fail-dep/concurrency/libc_pthread_join_main.rs +++ b/tests/fail-dep/concurrency/libc_pthread_join_main.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining the main thread is undefined behavior. diff --git a/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs b/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs index 966f416eea..f5b687a623 100644 --- a/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs +++ b/tests/fail-dep/concurrency/libc_pthread_join_multiple.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // Joining the same thread from multiple threads is undefined behavior. diff --git a/tests/fail-dep/concurrency/libc_pthread_join_self.rs b/tests/fail-dep/concurrency/libc_pthread_join_self.rs index 0c25c690f3..4bc1c82a25 100644 --- a/tests/fail-dep/concurrency/libc_pthread_join_self.rs +++ b/tests/fail-dep/concurrency/libc_pthread_join_self.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // We are making scheduler assumptions here. //@compile-flags: -Zmiri-preemption-rate=0 diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs similarity index 90% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs index 8b25107338..0a494c53b4 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // // Check that if we pass NULL attribute, then we get the default mutex type. diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_NULL_deadlock.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs index 60d56d41fd..0328115c63 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: deadlock use std::cell::UnsafeCell; diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_deadlock.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs similarity index 91% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs index f443768819..1038b8988f 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // // Check that if we do not set the mutex type, it is the default. diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_default_deadlock.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs similarity index 92% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs index ec3965c757..e474712cfd 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { unsafe { diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_destroy_locked.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs index 622c3eaeae..46f0c5f8d7 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_mutex twice fails, even without a check for number validity diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_double_destroy.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs similarity index 92% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs index 5ea09fa5aa..f311934e28 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { unsafe { diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_normal_deadlock.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs similarity index 92% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs index 8ce7542edb..2b5886dc16 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { unsafe { diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_normal_unlock_unlocked.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs b/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs rename to tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs index b56775252e..686a394f7c 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.stderr b/tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutex_wrong_owner.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs b/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs similarity index 91% rename from tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs rename to tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs index 474a277516..51f00d62b7 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.rs +++ b/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity diff --git a/tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.stderr b/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_mutexattr_double_destroy.stderr rename to tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs similarity index 83% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs index 603580ff58..fa4575bc1d 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_read_locked.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs similarity index 83% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs index ae44f22d14..e734a62bca 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_destroy_write_locked.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs similarity index 89% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs index 800986f750..e96f7fc680 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_double_destroy.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs similarity index 82% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs index 782c95b6d2..dffeee2b79 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs index 1b498ad8fc..328372b22e 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_read_wrong_owner.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs similarity index 78% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs index 05f7e7a06c..ced6b7a4f6 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_unlock_unlocked.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs index 0f02c3231a..4174751926 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: deadlock use std::cell::UnsafeCell; diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs similarity index 82% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs index 538f14ef89..099b8dcd10 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs index 10be5b3375..43b3ab09bb 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@error-in-other-file: deadlock use std::cell::UnsafeCell; diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs similarity index 82% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs index 2c963d3651..2704ff1544 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows fn main() { let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER); diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.stderr diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs similarity index 93% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs index dd099474d8..9a2cd09f08 100644 --- a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.rs +++ b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::cell::UnsafeCell; use std::sync::Arc; diff --git a/tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.stderr b/tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.stderr similarity index 100% rename from tests/fail-dep/shims/sync/libc_pthread_rwlock_write_wrong_owner.stderr rename to tests/fail-dep/concurrency/libc_pthread_rwlock_write_wrong_owner.stderr diff --git a/tests/fail-dep/shims/env-set_var-data-race.rs b/tests/fail-dep/libc/env-set_var-data-race.rs similarity index 90% rename from tests/fail-dep/shims/env-set_var-data-race.rs rename to tests/fail-dep/libc/env-set_var-data-race.rs index 2b9e7a34d6..a1895feb95 100644 --- a/tests/fail-dep/shims/env-set_var-data-race.rs +++ b/tests/fail-dep/libc/env-set_var-data-race.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc env support on Windows use std::env; use std::thread; diff --git a/tests/fail-dep/shims/env-set_var-data-race.stderr b/tests/fail-dep/libc/env-set_var-data-race.stderr similarity index 100% rename from tests/fail-dep/shims/env-set_var-data-race.stderr rename to tests/fail-dep/libc/env-set_var-data-race.stderr diff --git a/tests/fail-dep/shims/fs/close_stdout.rs b/tests/fail-dep/libc/fs/close_stdout.rs similarity index 82% rename from tests/fail-dep/shims/fs/close_stdout.rs rename to tests/fail-dep/libc/fs/close_stdout.rs index 09da8509af..42b7e2b783 100644 --- a/tests/fail-dep/shims/fs/close_stdout.rs +++ b/tests/fail-dep/libc/fs/close_stdout.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows //@compile-flags: -Zmiri-disable-isolation // FIXME: standard handles cannot be closed (https://github.com/rust-lang/rust/issues/40032) diff --git a/tests/fail-dep/shims/fs/close_stdout.stderr b/tests/fail-dep/libc/fs/close_stdout.stderr similarity index 100% rename from tests/fail-dep/shims/fs/close_stdout.stderr rename to tests/fail-dep/libc/fs/close_stdout.stderr diff --git a/tests/fail-dep/shims/fs/isolated_stdin.rs b/tests/fail-dep/libc/fs/isolated_stdin.rs similarity index 83% rename from tests/fail-dep/shims/fs/isolated_stdin.rs rename to tests/fail-dep/libc/fs/isolated_stdin.rs index a45f805696..3c62015a05 100644 --- a/tests/fail-dep/shims/fs/isolated_stdin.rs +++ b/tests/fail-dep/libc/fs/isolated_stdin.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; diff --git a/tests/fail-dep/shims/fs/isolated_stdin.stderr b/tests/fail-dep/libc/fs/isolated_stdin.stderr similarity index 100% rename from tests/fail-dep/shims/fs/isolated_stdin.stderr rename to tests/fail-dep/libc/fs/isolated_stdin.stderr diff --git a/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs b/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs similarity index 86% rename from tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs rename to tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs index ba9f404d7c..6d951a3a7b 100644 --- a/tests/fail-dep/shims/fs/mkstemp_immutable_arg.rs +++ b/tests/fail-dep/libc/fs/mkstemp_immutable_arg.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mkstemp on Windows //@compile-flags: -Zmiri-disable-isolation fn main() { diff --git a/tests/fail-dep/shims/fs/mkstemp_immutable_arg.stderr b/tests/fail-dep/libc/fs/mkstemp_immutable_arg.stderr similarity index 100% rename from tests/fail-dep/shims/fs/mkstemp_immutable_arg.stderr rename to tests/fail-dep/libc/fs/mkstemp_immutable_arg.stderr diff --git a/tests/fail-dep/shims/fs/read_from_stdout.rs b/tests/fail-dep/libc/fs/read_from_stdout.rs similarity index 83% rename from tests/fail-dep/shims/fs/read_from_stdout.rs rename to tests/fail-dep/libc/fs/read_from_stdout.rs index 073fca4712..624f584a0c 100644 --- a/tests/fail-dep/shims/fs/read_from_stdout.rs +++ b/tests/fail-dep/libc/fs/read_from_stdout.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows fn main() -> std::io::Result<()> { let mut bytes = [0u8; 512]; diff --git a/tests/fail-dep/shims/fs/read_from_stdout.stderr b/tests/fail-dep/libc/fs/read_from_stdout.stderr similarity index 100% rename from tests/fail-dep/shims/fs/read_from_stdout.stderr rename to tests/fail-dep/libc/fs/read_from_stdout.stderr diff --git a/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs b/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs similarity index 89% rename from tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs rename to tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs index ae231d4be6..d783967f95 100644 --- a/tests/fail-dep/shims/fs/unix_open_missing_required_mode.rs +++ b/tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows //@compile-flags: -Zmiri-disable-isolation fn main() { diff --git a/tests/fail-dep/shims/fs/unix_open_missing_required_mode.stderr b/tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr similarity index 100% rename from tests/fail-dep/shims/fs/unix_open_missing_required_mode.stderr rename to tests/fail-dep/libc/fs/unix_open_missing_required_mode.stderr diff --git a/tests/fail-dep/shims/fs/write_to_stdin.rs b/tests/fail-dep/libc/fs/write_to_stdin.rs similarity index 80% rename from tests/fail-dep/shims/fs/write_to_stdin.rs rename to tests/fail-dep/libc/fs/write_to_stdin.rs index d039ad718d..683c55e90e 100644 --- a/tests/fail-dep/shims/fs/write_to_stdin.rs +++ b/tests/fail-dep/libc/fs/write_to_stdin.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc IO on Windows fn main() -> std::io::Result<()> { let bytes = b"hello"; diff --git a/tests/fail-dep/shims/fs/write_to_stdin.stderr b/tests/fail-dep/libc/fs/write_to_stdin.stderr similarity index 100% rename from tests/fail-dep/shims/fs/write_to_stdin.stderr rename to tests/fail-dep/libc/fs/write_to_stdin.stderr diff --git a/tests/fail-dep/shims/memchr_null.rs b/tests/fail-dep/libc/memchr_null.rs similarity index 77% rename from tests/fail-dep/shims/memchr_null.rs rename to tests/fail-dep/libc/memchr_null.rs index 6bc7af7e6b..672cc10cd6 100644 --- a/tests/fail-dep/shims/memchr_null.rs +++ b/tests/fail-dep/libc/memchr_null.rs @@ -1,5 +1,3 @@ -//@ignore-target-windows: No libc on Windows - use std::ptr; // null is explicitly called out as UB in the C docs. diff --git a/tests/fail-dep/shims/memchr_null.stderr b/tests/fail-dep/libc/memchr_null.stderr similarity index 100% rename from tests/fail-dep/shims/memchr_null.stderr rename to tests/fail-dep/libc/memchr_null.stderr diff --git a/tests/fail-dep/shims/memcmp_null.rs b/tests/fail-dep/libc/memcmp_null.rs similarity index 78% rename from tests/fail-dep/shims/memcmp_null.rs rename to tests/fail-dep/libc/memcmp_null.rs index a4e0034c40..066af4a8ae 100644 --- a/tests/fail-dep/shims/memcmp_null.rs +++ b/tests/fail-dep/libc/memcmp_null.rs @@ -1,5 +1,3 @@ -//@ignore-target-windows: No libc on Windows - use std::ptr; // null is explicitly called out as UB in the C docs. diff --git a/tests/fail-dep/shims/memcmp_null.stderr b/tests/fail-dep/libc/memcmp_null.stderr similarity index 100% rename from tests/fail-dep/shims/memcmp_null.stderr rename to tests/fail-dep/libc/memcmp_null.stderr diff --git a/tests/fail-dep/shims/memcmp_zero.rs b/tests/fail-dep/libc/memcmp_zero.rs similarity index 90% rename from tests/fail-dep/shims/memcmp_zero.rs rename to tests/fail-dep/libc/memcmp_zero.rs index f2ddc20056..e28aa26270 100644 --- a/tests/fail-dep/shims/memcmp_zero.rs +++ b/tests/fail-dep/libc/memcmp_zero.rs @@ -1,4 +1,3 @@ -//@ignore-target-windows: No libc on Windows //@compile-flags: -Zmiri-permissive-provenance // C says that passing "invalid" pointers is UB for all string functions. diff --git a/tests/fail-dep/shims/memcmp_zero.stderr b/tests/fail-dep/libc/memcmp_zero.stderr similarity index 100% rename from tests/fail-dep/shims/memcmp_zero.stderr rename to tests/fail-dep/libc/memcmp_zero.stderr diff --git a/tests/fail-dep/shims/memcpy_zero.rs b/tests/fail-dep/libc/memcpy_zero.rs similarity index 88% rename from tests/fail-dep/shims/memcpy_zero.rs rename to tests/fail-dep/libc/memcpy_zero.rs index 5283fea4cb..b37ed7df74 100644 --- a/tests/fail-dep/shims/memcpy_zero.rs +++ b/tests/fail-dep/libc/memcpy_zero.rs @@ -1,4 +1,3 @@ -//@ignore-target-windows: No libc on Windows //@compile-flags: -Zmiri-permissive-provenance // C's memcpy is 0 bytes is UB for some pointers that are allowed in Rust's `copy_nonoverlapping`. diff --git a/tests/fail-dep/shims/memcpy_zero.stderr b/tests/fail-dep/libc/memcpy_zero.stderr similarity index 100% rename from tests/fail-dep/shims/memcpy_zero.stderr rename to tests/fail-dep/libc/memcpy_zero.stderr diff --git a/tests/fail-dep/shims/memrchr_null.rs b/tests/fail-dep/libc/memrchr_null.rs similarity index 81% rename from tests/fail-dep/shims/memrchr_null.rs rename to tests/fail-dep/libc/memrchr_null.rs index b6707d558d..f06336b129 100644 --- a/tests/fail-dep/shims/memrchr_null.rs +++ b/tests/fail-dep/libc/memrchr_null.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No `memrchr` on Windows //@ignore-target-apple: No `memrchr` on some apple targets use std::ptr; diff --git a/tests/fail-dep/shims/memrchr_null.stderr b/tests/fail-dep/libc/memrchr_null.stderr similarity index 100% rename from tests/fail-dep/shims/memrchr_null.stderr rename to tests/fail-dep/libc/memrchr_null.stderr diff --git a/tests/fail-dep/shims/mmap_invalid_dealloc.rs b/tests/fail-dep/libc/mmap_invalid_dealloc.rs similarity index 90% rename from tests/fail-dep/shims/mmap_invalid_dealloc.rs rename to tests/fail-dep/libc/mmap_invalid_dealloc.rs index 70f7a6a7ce..9d55a49355 100644 --- a/tests/fail-dep/shims/mmap_invalid_dealloc.rs +++ b/tests/fail-dep/libc/mmap_invalid_dealloc.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows #![feature(rustc_private)] diff --git a/tests/fail-dep/shims/mmap_invalid_dealloc.stderr b/tests/fail-dep/libc/mmap_invalid_dealloc.stderr similarity index 100% rename from tests/fail-dep/shims/mmap_invalid_dealloc.stderr rename to tests/fail-dep/libc/mmap_invalid_dealloc.stderr diff --git a/tests/fail-dep/shims/mmap_use_after_munmap.rs b/tests/fail-dep/libc/mmap_use_after_munmap.rs similarity index 90% rename from tests/fail-dep/shims/mmap_use_after_munmap.rs rename to tests/fail-dep/libc/mmap_use_after_munmap.rs index c97b013ba5..05ac232f5d 100644 --- a/tests/fail-dep/shims/mmap_use_after_munmap.rs +++ b/tests/fail-dep/libc/mmap_use_after_munmap.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows #![feature(rustc_private)] diff --git a/tests/fail-dep/shims/mmap_use_after_munmap.stderr b/tests/fail-dep/libc/mmap_use_after_munmap.stderr similarity index 100% rename from tests/fail-dep/shims/mmap_use_after_munmap.stderr rename to tests/fail-dep/libc/mmap_use_after_munmap.stderr diff --git a/tests/fail-dep/shims/munmap_partial.rs b/tests/fail-dep/libc/munmap_partial.rs similarity index 94% rename from tests/fail-dep/shims/munmap_partial.rs rename to tests/fail-dep/libc/munmap_partial.rs index d7aef47235..4386dc71af 100644 --- a/tests/fail-dep/shims/munmap_partial.rs +++ b/tests/fail-dep/libc/munmap_partial.rs @@ -1,7 +1,7 @@ //! The man pages for mmap/munmap suggest that it is possible to partly unmap a previously-mapped //! region of address space, but to LLVM that would be partial deallocation, which LLVM does not //! support. So even though the man pages say this sort of use is possible, we must report UB. -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows //@normalize-stderr-test: "size [0-9]+ and alignment" -> "size SIZE and alignment" fn main() { diff --git a/tests/fail-dep/shims/munmap_partial.stderr b/tests/fail-dep/libc/munmap_partial.stderr similarity index 100% rename from tests/fail-dep/shims/munmap_partial.stderr rename to tests/fail-dep/libc/munmap_partial.stderr diff --git a/tests/fail-dep/realloc-zero.rs b/tests/fail-dep/libc/realloc-zero.rs similarity index 81% rename from tests/fail-dep/realloc-zero.rs rename to tests/fail-dep/libc/realloc-zero.rs index 1482798e90..0e210f3135 100644 --- a/tests/fail-dep/realloc-zero.rs +++ b/tests/fail-dep/libc/realloc-zero.rs @@ -1,5 +1,3 @@ -//@ignore-target-windows: No libc on Windows - fn main() { unsafe { let p1 = libc::malloc(20); diff --git a/tests/fail-dep/realloc-zero.stderr b/tests/fail-dep/libc/realloc-zero.stderr similarity index 100% rename from tests/fail-dep/realloc-zero.stderr rename to tests/fail-dep/libc/realloc-zero.stderr diff --git a/tests/fail-dep/unsupported_incomplete_function.rs b/tests/fail-dep/libc/unsupported_incomplete_function.rs similarity index 87% rename from tests/fail-dep/unsupported_incomplete_function.rs rename to tests/fail-dep/libc/unsupported_incomplete_function.rs index 6ef842c9cc..cd8422f4af 100644 --- a/tests/fail-dep/unsupported_incomplete_function.rs +++ b/tests/fail-dep/libc/unsupported_incomplete_function.rs @@ -1,6 +1,6 @@ //! `signal()` is special on Linux and macOS that it's only supported within libstd. //! The implementation is not complete enough to permit user code to call it. -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No `libc::signal` on Windows //@normalize-stderr-test: "OS `.*`" -> "$$OS" fn main() { diff --git a/tests/fail-dep/unsupported_incomplete_function.stderr b/tests/fail-dep/libc/unsupported_incomplete_function.stderr similarity index 100% rename from tests/fail-dep/unsupported_incomplete_function.stderr rename to tests/fail-dep/libc/unsupported_incomplete_function.stderr diff --git a/tests/panic/unsupported_syscall.rs b/tests/panic/unsupported_syscall.rs index 31d666e1d9..30f9da5f80 100644 --- a/tests/panic/unsupported_syscall.rs +++ b/tests/panic/unsupported_syscall.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: no `syscall` on Windows //@ignore-target-apple: `syscall` is not supported on macOS //@compile-flags: -Zmiri-panic-on-unsupported diff --git a/tests/pass-dep/calloc.rs b/tests/pass-dep/calloc.rs deleted file mode 100644 index 62ab63c5fc..0000000000 --- a/tests/pass-dep/calloc.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@ignore-target-windows: No libc on Windows - -use core::slice; - -fn main() { - unsafe { - let p1 = libc::calloc(0, 0); - assert!(p1.is_null()); - - let p2 = libc::calloc(20, 0); - assert!(p2.is_null()); - - let p3 = libc::calloc(0, 20); - assert!(p3.is_null()); - - let p4 = libc::calloc(4, 8); - assert!(!p4.is_null()); - let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8); - assert_eq!(&slice, &[0_u8; 4 * 8]); - libc::free(p4); - } -} diff --git a/tests/pass-dep/shims/env-cleanup-data-race.rs b/tests/pass-dep/concurrency/env-cleanup-data-race.rs similarity index 93% rename from tests/pass-dep/shims/env-cleanup-data-race.rs rename to tests/pass-dep/concurrency/env-cleanup-data-race.rs index 5c29a1b15a..86a47ba365 100644 --- a/tests/pass-dep/shims/env-cleanup-data-race.rs +++ b/tests/pass-dep/concurrency/env-cleanup-data-race.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -Zmiri-preemption-rate=0 -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No libc env support on Windows use std::ffi::CStr; use std::thread; diff --git a/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs b/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs index f362caa11d..d758168c7c 100644 --- a/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs +++ b/tests/pass-dep/concurrency/libc_pthread_cond_timedwait.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS. //@compile-flags: -Zmiri-disable-isolation diff --git a/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs b/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs index 66c0895a5d..f1a3c5dc10 100644 --- a/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs +++ b/tests/pass-dep/concurrency/libc_pthread_cond_timedwait_isolated.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //@ignore-target-apple: pthread_condattr_setclock is not supported on MacOS. /// Test that conditional variable timeouts are working properly diff --git a/tests/pass-dep/concurrency/tls_pthread_drop_order.rs b/tests/pass-dep/concurrency/tls_pthread_drop_order.rs index ae874740f2..0eaab96764 100644 --- a/tests/pass-dep/concurrency/tls_pthread_drop_order.rs +++ b/tests/pass-dep/concurrency/tls_pthread_drop_order.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows //! Test that pthread_key destructors are run in the right order. //! Note that these are *not* used by actual `thread_local!` on Linux! Those use //! `thread_local_dtor::register_dtor` from the stdlib instead. In Miri this hits the fallback path diff --git a/tests/pass-dep/extra_fn_ptr_gc.rs b/tests/pass-dep/extra_fn_ptr_gc.rs index 716119a0fc..1198168795 100644 --- a/tests/pass-dep/extra_fn_ptr_gc.rs +++ b/tests/pass-dep/extra_fn_ptr_gc.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No `dlsym` on Windows //@compile-flags: -Zmiri-permissive-provenance #[path = "../utils/mod.rs"] diff --git a/tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs b/tests/pass-dep/libc/fcntl_f-fullfsync_apple.rs similarity index 100% rename from tests/pass-dep/shims/fcntl_f-fullfsync_apple.rs rename to tests/pass-dep/libc/fcntl_f-fullfsync_apple.rs diff --git a/tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr b/tests/pass-dep/libc/fcntl_f-fullfsync_apple.stderr similarity index 100% rename from tests/pass-dep/shims/fcntl_f-fullfsync_apple.stderr rename to tests/pass-dep/libc/fcntl_f-fullfsync_apple.stderr diff --git a/tests/pass-dep/shims/libc-fs-with-isolation.rs b/tests/pass-dep/libc/libc-fs-with-isolation.rs similarity index 100% rename from tests/pass-dep/shims/libc-fs-with-isolation.rs rename to tests/pass-dep/libc/libc-fs-with-isolation.rs diff --git a/tests/pass-dep/shims/libc-fs-with-isolation.stderr b/tests/pass-dep/libc/libc-fs-with-isolation.stderr similarity index 100% rename from tests/pass-dep/shims/libc-fs-with-isolation.stderr rename to tests/pass-dep/libc/libc-fs-with-isolation.stderr diff --git a/tests/pass-dep/shims/libc-fs.rs b/tests/pass-dep/libc/libc-fs.rs similarity index 100% rename from tests/pass-dep/shims/libc-fs.rs rename to tests/pass-dep/libc/libc-fs.rs diff --git a/tests/pass-dep/shims/libc-fs.stderr b/tests/pass-dep/libc/libc-fs.stderr similarity index 100% rename from tests/pass-dep/shims/libc-fs.stderr rename to tests/pass-dep/libc/libc-fs.stderr diff --git a/tests/pass-dep/shims/libc-fs.stdout b/tests/pass-dep/libc/libc-fs.stdout similarity index 100% rename from tests/pass-dep/shims/libc-fs.stdout rename to tests/pass-dep/libc/libc-fs.stdout diff --git a/tests/pass-dep/shims/libc-misc.rs b/tests/pass-dep/libc/libc-mem.rs similarity index 72% rename from tests/pass-dep/shims/libc-misc.rs rename to tests/pass-dep/libc/libc-mem.rs index 32898d4959..33cdc18b70 100644 --- a/tests/pass-dep/shims/libc-misc.rs +++ b/tests/pass-dep/libc/libc-mem.rs @@ -1,33 +1,5 @@ -//@ignore-target-windows: No libc on Windows -//@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(pointer_is_aligned_to)] -#![feature(strict_provenance)] - -use std::mem::{self, transmute}; -use std::ptr; - -/// Tests whether each thread has its own `__errno_location`. -fn test_thread_local_errno() { - #[cfg(any(target_os = "illumos", target_os = "solaris"))] - use libc::___errno as __errno_location; - #[cfg(target_os = "linux")] - use libc::__errno_location; - #[cfg(any(target_os = "macos", target_os = "freebsd"))] - use libc::__error as __errno_location; - - unsafe { - *__errno_location() = 0xBEEF; - std::thread::spawn(|| { - assert_eq!(*__errno_location(), 0); - *__errno_location() = 0xBAD1DEA; - assert_eq!(*__errno_location(), 0xBAD1DEA); - }) - .join() - .unwrap(); - assert_eq!(*__errno_location(), 0xBEEF); - } -} +#![feature(strict_provenance, pointer_is_aligned_to)] +use std::{mem, ptr, slice}; fn test_memcpy() { unsafe { @@ -106,49 +78,71 @@ fn test_strcpy() { } } -#[cfg(target_os = "linux")] -fn test_sigrt() { - let min = libc::SIGRTMIN(); - let max = libc::SIGRTMAX(); - - // "The Linux kernel supports a range of 33 different real-time - // signals, numbered 32 to 64" - assert!(min >= 32); - assert!(max >= 32); - assert!(min <= 64); - assert!(max <= 64); - - // "POSIX.1-2001 requires that an implementation support at least - // _POSIX_RTSIG_MAX (8) real-time signals." - assert!(min < max); - assert!(max - min >= 8) -} +fn test_malloc() { + // Test that small allocations sometimes *are* not very aligned. + let saw_unaligned = (0..64).any(|_| unsafe { + let p = libc::malloc(3); + libc::free(p); + (p as usize) % 4 != 0 // find any that this is *not* 4-aligned + }); + assert!(saw_unaligned); + + unsafe { + let p1 = libc::malloc(20); + p1.write_bytes(0u8, 20); -fn test_dlsym() { - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) }; - assert!(addr as usize == 0); + // old size < new size + let p2 = libc::realloc(p1, 40); + let slice = slice::from_raw_parts(p2 as *const u8, 20); + assert_eq!(&slice, &[0_u8; 20]); - let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) }; - assert!(addr as usize != 0); - let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) }; - assert_eq!(isatty(999), 0); - let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); - assert_eq!(errno, libc::EBADF); + // old size == new size + let p3 = libc::realloc(p2, 40); + let slice = slice::from_raw_parts(p3 as *const u8, 20); + assert_eq!(&slice, &[0_u8; 20]); + + // old size > new size + let p4 = libc::realloc(p3, 10); + let slice = slice::from_raw_parts(p4 as *const u8, 10); + assert_eq!(&slice, &[0_u8; 10]); + + libc::free(p4); + } + + unsafe { + // Realloc with size 0 is okay for the null pointer + let p2 = libc::realloc(ptr::null_mut(), 0); + assert!(p2.is_null()); + } + + unsafe { + let p1 = libc::realloc(ptr::null_mut(), 20); + assert!(!p1.is_null()); + + libc::free(p1); + } } -#[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] -fn test_reallocarray() { +fn test_calloc() { unsafe { - let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); - assert!(!p.is_null()); - libc::free(p); - p = libc::malloc(16); - let r = libc::reallocarray(p, 2, 32); - assert!(!r.is_null()); - libc::free(r); + let p1 = libc::calloc(0, 0); + assert!(p1.is_null()); + + let p2 = libc::calloc(20, 0); + assert!(p2.is_null()); + + let p3 = libc::calloc(0, 20); + assert!(p3.is_null()); + + let p4 = libc::calloc(4, 8); + assert!(!p4.is_null()); + let slice = slice::from_raw_parts(p4 as *const u8, 4 * 8); + assert_eq!(&slice, &[0_u8; 4 * 8]); + libc::free(p4); } } +#[cfg(not(target_os = "windows"))] fn test_memalign() { // A normal allocation. unsafe { @@ -225,18 +219,37 @@ fn test_memalign() { } } -fn main() { - test_thread_local_errno(); - - test_dlsym(); - - test_memcpy(); - test_strcpy(); +#[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "illumos", + target_os = "solaris" +)))] +fn test_reallocarray() { + unsafe { + let mut p = libc::reallocarray(std::ptr::null_mut(), 4096, 2); + assert!(!p.is_null()); + libc::free(p); + p = libc::malloc(16); + let r = libc::reallocarray(p, 2, 32); + assert!(!r.is_null()); + libc::free(r); + } +} +fn main() { + test_malloc(); + test_calloc(); + #[cfg(not(target_os = "windows"))] test_memalign(); - #[cfg(not(any(target_os = "macos", target_os = "illumos", target_os = "solaris")))] + #[cfg(not(any( + target_os = "windows", + target_os = "macos", + target_os = "illumos", + target_os = "solaris" + )))] test_reallocarray(); - #[cfg(target_os = "linux")] - test_sigrt(); + test_memcpy(); + test_strcpy(); } diff --git a/tests/pass-dep/libc/libc-misc.rs b/tests/pass-dep/libc/libc-misc.rs new file mode 100644 index 0000000000..736e0bf8eb --- /dev/null +++ b/tests/pass-dep/libc/libc-misc.rs @@ -0,0 +1,68 @@ +//@ignore-target-windows: only very limited libc on Windows +//@compile-flags: -Zmiri-disable-isolation +#![feature(io_error_more)] +#![feature(pointer_is_aligned_to)] +#![feature(strict_provenance)] + +use std::mem::transmute; + +/// Tests whether each thread has its own `__errno_location`. +fn test_thread_local_errno() { + #[cfg(any(target_os = "illumos", target_os = "solaris"))] + use libc::___errno as __errno_location; + #[cfg(target_os = "linux")] + use libc::__errno_location; + #[cfg(any(target_os = "macos", target_os = "freebsd"))] + use libc::__error as __errno_location; + + unsafe { + *__errno_location() = 0xBEEF; + std::thread::spawn(|| { + assert_eq!(*__errno_location(), 0); + *__errno_location() = 0xBAD1DEA; + assert_eq!(*__errno_location(), 0xBAD1DEA); + }) + .join() + .unwrap(); + assert_eq!(*__errno_location(), 0xBEEF); + } +} + +#[cfg(target_os = "linux")] +fn test_sigrt() { + let min = libc::SIGRTMIN(); + let max = libc::SIGRTMAX(); + + // "The Linux kernel supports a range of 33 different real-time + // signals, numbered 32 to 64" + assert!(min >= 32); + assert!(max >= 32); + assert!(min <= 64); + assert!(max <= 64); + + // "POSIX.1-2001 requires that an implementation support at least + // _POSIX_RTSIG_MAX (8) real-time signals." + assert!(min < max); + assert!(max - min >= 8) +} + +fn test_dlsym() { + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"notasymbol\0".as_ptr().cast()) }; + assert!(addr as usize == 0); + + let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, b"isatty\0".as_ptr().cast()) }; + assert!(addr as usize != 0); + let isatty: extern "C" fn(i32) -> i32 = unsafe { transmute(addr) }; + assert_eq!(isatty(999), 0); + let errno = std::io::Error::last_os_error().raw_os_error().unwrap(); + assert_eq!(errno, libc::EBADF); +} + +fn main() { + test_thread_local_errno(); + + test_dlsym(); + + #[cfg(target_os = "linux")] + test_sigrt(); +} diff --git a/tests/pass-dep/shims/libc-random.rs b/tests/pass-dep/libc/libc-random.rs similarity index 100% rename from tests/pass-dep/shims/libc-random.rs rename to tests/pass-dep/libc/libc-random.rs diff --git a/tests/pass-dep/shims/libc-time.rs b/tests/pass-dep/libc/libc-time.rs similarity index 100% rename from tests/pass-dep/shims/libc-time.rs rename to tests/pass-dep/libc/libc-time.rs diff --git a/tests/pass-dep/shims/mmap.rs b/tests/pass-dep/libc/mmap.rs similarity index 99% rename from tests/pass-dep/shims/mmap.rs rename to tests/pass-dep/libc/mmap.rs index 5acdedc67b..a0787c6890 100644 --- a/tests/pass-dep/shims/mmap.rs +++ b/tests/pass-dep/libc/mmap.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No mmap on Windows //@compile-flags: -Zmiri-disable-isolation -Zmiri-permissive-provenance #![feature(strict_provenance)] diff --git a/tests/pass-dep/shims/pthread-sync.rs b/tests/pass-dep/libc/pthread-sync.rs similarity index 99% rename from tests/pass-dep/shims/pthread-sync.rs rename to tests/pass-dep/libc/pthread-sync.rs index 12d3f2b6f1..1427526212 100644 --- a/tests/pass-dep/shims/pthread-sync.rs +++ b/tests/pass-dep/libc/pthread-sync.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows // We use `yield` to test specific interleavings, so disable automatic preemption. //@compile-flags: -Zmiri-preemption-rate=0 #![feature(sync_unsafe_cell)] diff --git a/tests/pass-dep/shims/pthread-threadname.rs b/tests/pass-dep/libc/pthread-threadname.rs similarity index 97% rename from tests/pass-dep/shims/pthread-threadname.rs rename to tests/pass-dep/libc/pthread-threadname.rs index bc782044d4..4c4f542dfd 100644 --- a/tests/pass-dep/shims/pthread-threadname.rs +++ b/tests/pass-dep/libc/pthread-threadname.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: No libc on Windows +//@ignore-target-windows: No pthreads on Windows use std::ffi::CStr; #[cfg(not(target_os = "freebsd"))] use std::ffi::CString; diff --git a/tests/pass-dep/malloc.rs b/tests/pass-dep/malloc.rs deleted file mode 100644 index 35cd137931..0000000000 --- a/tests/pass-dep/malloc.rs +++ /dev/null @@ -1,48 +0,0 @@ -//@ignore-target-windows: No libc on Windows - -use core::{ptr, slice}; - -fn main() { - // Test that small allocations sometimes *are* not very aligned. - let saw_unaligned = (0..64).any(|_| unsafe { - let p = libc::malloc(3); - libc::free(p); - (p as usize) % 4 != 0 // find any that this is *not* 4-aligned - }); - assert!(saw_unaligned); - - unsafe { - // Use calloc for initialized memory - let p1 = libc::calloc(20, 1); - - // old size < new size - let p2 = libc::realloc(p1, 40); - let slice = slice::from_raw_parts(p2 as *const u8, 20); - assert_eq!(&slice, &[0_u8; 20]); - - // old size == new size - let p3 = libc::realloc(p2, 40); - let slice = slice::from_raw_parts(p3 as *const u8, 20); - assert_eq!(&slice, &[0_u8; 20]); - - // old size > new size - let p4 = libc::realloc(p3, 10); - let slice = slice::from_raw_parts(p4 as *const u8, 10); - assert_eq!(&slice, &[0_u8; 10]); - - libc::free(p4); - } - - unsafe { - // Realloc with size 0 is okay for the null pointer - let p2 = libc::realloc(ptr::null_mut(), 0); - assert!(p2.is_null()); - } - - unsafe { - let p1 = libc::realloc(ptr::null_mut(), 20); - assert!(!p1.is_null()); - - libc::free(p1); - } -} From ab6cf219c4ec0d4dba9f293c2ccd71b583a9338b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 7 May 2024 20:18:39 +0200 Subject: [PATCH 181/208] README: update introduction --- README.md | 70 ++++++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index d086987f43..3b2f3ea34f 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,34 @@ # Miri -An experimental interpreter for [Rust][rust]'s -[mid-level intermediate representation][mir] (MIR). It can run binaries and -test suites of cargo projects and detect certain classes of -[undefined behavior](https://doc.rust-lang.org/reference/behavior-considered-undefined.html), -for example: +Miri is an [Undefined Behavior][reference-ub] detection tool for Rust. It can run binaries and test +suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For +instance: * Out-of-bounds memory accesses and use-after-free * Invalid use of uninitialized data * Violation of intrinsic preconditions (an [`unreachable_unchecked`] being reached, calling [`copy_nonoverlapping`] with overlapping ranges, ...) * Not sufficiently aligned memory accesses and references -* Violation of *some* basic type invariants (a `bool` that is not 0 or 1, for example, +* Violation of basic type invariants (a `bool` that is not 0 or 1, for example, or an invalid enum discriminant) * **Experimental**: Violations of the [Stacked Borrows] rules governing aliasing for reference types * **Experimental**: Violations of the [Tree Borrows] aliasing rules, as an optional alternative to [Stacked Borrows] -* **Experimental**: Data races +* **Experimental**: Data races and emulation of weak memory effects, i.e., + atomic reads can return outdated values. On top of that, Miri will also tell you about memory leaks: when there is memory still allocated at the end of the execution, and that memory is not reachable from a global `static`, Miri will raise an error. -Miri supports almost all Rust language features; in particular, unwinding and -concurrency are properly supported (including some experimental emulation of -weak memory effects, i.e., reads can return outdated values). - You can use Miri to emulate programs on other targets, e.g. to ensure that byte-level data manipulation works correctly both on little-endian and big-endian systems. See [cross-interpretation](#cross-interpretation-running-for-different-targets) below. -Miri has already discovered some [real-world bugs](#bugs-found-by-miri). If you +Miri has already discovered many [real-world bugs](#bugs-found-by-miri). If you found a bug with Miri, we'd appreciate if you tell us and we'll add it to the list! @@ -45,24 +40,27 @@ clocks, are replaced by deterministic "fake" implementations. Set (In particular, the "fake" system RNG APIs make Miri **not suited for cryptographic use**! Do not generate keys using Miri.) -All that said, be aware that Miri will **not catch all cases of undefined -behavior** in your program, and cannot run all programs: +All that said, be aware that Miri does **not catch every violation of the Rust specification** in +your program, not least because there is no such specification. Miri uses its own approximation of +what is and is not Undefined Behavior in Rust. To the best of our knowledge, all Undefined Behavior +that has the potential to affect a program's correctness *is* being detected by Miri (modulo +[bugs][I-misses-ub]), but you should consult [the Reference][reference-ub] for the official +definition of Undefined Behavior. Miri will be updated with the Rust compiler to protect against UB +as it is understood by the current compiler, but it makes no promises about future versions of +rustc. -* There are still plenty of open questions around the basic invariants for some - types and when these invariants even have to hold. Miri tries to avoid false - positives here, so if your program runs fine in Miri right now that is by no - means a guarantee that it is UB-free when these questions get answered. +Further caveats that Miri users should be aware of: - In particular, Miri does not check that references point to valid data. * If the program relies on unspecified details of how data is laid out, it will still run fine in Miri -- but might break (including causing UB) on different - compiler versions or different platforms. + compiler versions or different platforms. (You can use `-Zrandomize-layout` + to detect some of these cases.) * Program execution is non-deterministic when it depends, for example, on where exactly in memory allocations end up, or on the exact interleaving of concurrent threads. Miri tests one of many possible executions of your - program. You can alleviate this to some extent by running Miri with different - values for `-Zmiri-seed`, but that will still by far not explore all possible - executions. + program, but it will miss bugs that only occur in a different possible execution. + You can alleviate this to some extent by running Miri with different + values for `-Zmiri-seed`, but that will still by far not explore all possible executions. * Miri runs the program as a platform-independent interpreter, so the program has no access to most platform-specific APIs or FFI. A few APIs have been implemented (such as printing to stdout, accessing environment variables, and @@ -70,8 +68,8 @@ behavior** in your program, and cannot run all programs: not support networking. System API support varies between targets; if you run on Windows it is a good idea to use `--target x86_64-unknown-linux-gnu` to get better support. -* Weak memory emulation may [produce weak behaviours](https://github.com/rust-lang/miri/issues/2301) - unobservable by compiled programs running on real hardware when `SeqCst` fences are used, and it +* Weak memory emulation may [produce weak behaviors](https://github.com/rust-lang/miri/issues/2301) + when `SeqCst` fences are used that are not actually permitted by the Rust memory model, and it cannot produce all behaviors possibly observable on real hardware. Moreover, Miri fundamentally cannot tell you whether your code is *sound*. [Soundness] is the property @@ -87,6 +85,8 @@ coverage. [Stacked Borrows]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md [Tree Borrows]: https://perso.crans.org/vanille/treebor/ [Soundness]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library +[reference-ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html +[I-misses-ub]: https://github.com/rust-lang/miri/labels/I-misses-UB ## Using Miri @@ -97,14 +97,8 @@ Install Miri on Rust nightly via `rustup`: rustup +nightly component add miri ``` -If `rustup` says the `miri` component is unavailable, that's because not all -nightly releases come with all tools. Check out -[this website](https://rust-lang.github.io/rustup-components-history) to -determine a nightly version that comes with Miri and install that using `rustup -toolchain install nightly-YYYY-MM-DD`. Either way, all of the following commands -assume the right toolchain is pinned via `rustup override set nightly` or -`rustup override set nightly-YYYY-MM-DD`. (Alternatively, use `cargo -+nightly`/`cargo +nightly-YYYY-MM-DD` for each of the following commands.) +All the following commands assume the nightly toolchain is pinned via `rustup override set nightly`. +Alternatively, use `cargo +nightly` for each of the following commands. Now you can run your project in Miri: @@ -118,12 +112,12 @@ dependencies. It will ask you for confirmation before installing anything. example, `cargo miri test filter` only runs the tests containing `filter` in their name. -You can pass arguments to Miri via `MIRIFLAGS`. For example, +You can pass [flags][miri-flags] to Miri via `MIRIFLAGS`. For example, `MIRIFLAGS="-Zmiri-disable-stacked-borrows" cargo miri run` runs the program without checking the aliasing of references. When compiling code via `cargo miri`, the `cfg(miri)` config flag is set for code -that will be interpret under Miri. You can use this to ignore test cases that fail +that will be interpreted under Miri. You can use this to ignore test cases that fail under Miri because they do things Miri does not support: ```rust @@ -159,10 +153,8 @@ endian-sensitive code. ### Running Miri on CI -To run Miri on CI, make sure that you handle the case where the latest nightly -does not ship the Miri component because it currently does not build. `rustup -toolchain install --component` knows how to handle this situation, so the -following snippet should always work: +When running Miri on CI, use the following snippet to install a nightly toolchain with the Miri +component: ```sh rustup toolchain install nightly --component miri From abaa47f843d6c27ccfa9c06fe518a2d92bd05b6d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 7 May 2024 20:33:18 +0200 Subject: [PATCH 182/208] remove problems that I do not think we have seen in a while --- README.md | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/README.md b/README.md index 3b2f3ea34f..2c76749fbc 100644 --- a/README.md +++ b/README.md @@ -265,25 +265,12 @@ To get a backtrace, you need to disable isolation RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test ``` -#### "found possibly newer version of crate `std` which `` depends on" - -Your build directory may contain artifacts from an earlier build that have/have -not been built for Miri. Run `cargo clean` before switching from non-Miri to -Miri builds and vice-versa. - #### "found crate `std` compiled by an incompatible version of rustc" You may be running `cargo miri` with a different compiler version than the one used to build the custom libstd that Miri uses, and Miri failed to detect that. Try running `cargo miri clean`. -#### "no mir for `std::rt::lang_start_internal`" - -This means the sysroot you are using was not compiled with Miri in mind. This -should never happen when you use `cargo miri` because that takes care of setting -up the sysroot. If you are using `miri` (the Miri driver) directly, see the -[contributors' guide](CONTRIBUTING.md) for how to use `./miri` to best do that. - ## Miri `-Z` flags and environment variables [miri-flags]: #miri--z-flags-and-environment-variables From 0252629bd6b00f07d9c3a5ba01a58d2cf55390e8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 8 May 2024 16:56:02 +1000 Subject: [PATCH 183/208] Simplify `use crate::rustc_foo::bar` occurrences. They can just be written as `use rustc_foo::bar`, which is far more standard. (I didn't even know that a `crate::` prefix was valid.) --- src/shims/x86/avx2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shims/x86/avx2.rs b/src/shims/x86/avx2.rs index 783be802bb..bbde5b4958 100644 --- a/src/shims/x86/avx2.rs +++ b/src/shims/x86/avx2.rs @@ -1,5 +1,5 @@ -use crate::rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::mir; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; From 9bea173eef6ba8c7a33a6a76d91fce03b8befb07 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 8 May 2024 18:01:21 +0200 Subject: [PATCH 184/208] io::Error handling: keep around the full io::Error for longer so we can give better errors --- src/helpers.rs | 19 ++++++++----------- src/shims/unix/env.rs | 8 ++++---- src/shims/unix/fd.rs | 4 ++-- src/shims/unix/fs.rs | 24 ++++++++++++------------ src/shims/windows/env.rs | 8 ++++---- src/shims/windows/foreign_items.rs | 2 +- 6 files changed, 31 insertions(+), 34 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 795f21ddb9..4050ae3c4b 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -751,26 +751,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// This function tries to produce the most similar OS error from the `std::io::ErrorKind` /// as a platform-specific errnum. - fn io_error_to_errnum( - &self, - err_kind: std::io::ErrorKind, - ) -> InterpResult<'tcx, Scalar> { + fn io_error_to_errnum(&self, err: std::io::Error) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_ref(); let target = &this.tcx.sess.target; if target.families.iter().any(|f| f == "unix") { for &(name, kind) in UNIX_IO_ERROR_TABLE { - if err_kind == kind { + if err.kind() == kind { return Ok(this.eval_libc(name)); } } - throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind) + throw_unsup_format!("unsupported io error: {err}") } else if target.families.iter().any(|f| f == "windows") { for &(name, kind) in WINDOWS_IO_ERROR_TABLE { - if err_kind == kind { + if err.kind() == kind { return Ok(this.eval_windows("c", name)); } } - throw_unsup_format!("io error {:?} cannot be translated into a raw os error", err_kind); + throw_unsup_format!("unsupported io error: {err}"); } else { throw_unsup_format!( "converting io::Error into errnum is unsupported for OS {}", @@ -812,8 +809,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } /// Sets the last OS error using a `std::io::ErrorKind`. - fn set_last_error_from_io_error(&mut self, err_kind: std::io::ErrorKind) -> InterpResult<'tcx> { - self.set_last_error(self.io_error_to_errnum(err_kind)?) + fn set_last_error_from_io_error(&mut self, err: std::io::Error) -> InterpResult<'tcx> { + self.set_last_error(self.io_error_to_errnum(err)?) } /// Helper function that consumes an `std::io::Result` and returns an @@ -829,7 +826,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match result { Ok(ok) => Ok(ok), Err(e) => { - self.eval_context_mut().set_last_error_from_io_error(e.kind())?; + self.eval_context_mut().set_last_error_from_io_error(e)?; Ok((-1).into()) } } diff --git a/src/shims/unix/env.rs b/src/shims/unix/env.rs index 9082d13da8..6fcfb69391 100644 --- a/src/shims/unix/env.rs +++ b/src/shims/unix/env.rs @@ -228,7 +228,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`getcwd`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(Pointer::null()); } @@ -241,7 +241,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let erange = this.eval_libc("ERANGE"); this.set_last_error(erange)?; } - Err(e) => this.set_last_error_from_io_error(e.kind())?, + Err(e) => this.set_last_error_from_io_error(e)?, } Ok(Pointer::null()) @@ -255,7 +255,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`chdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -263,7 +263,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match env::set_current_dir(path) { Ok(()) => Ok(0), Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } diff --git a/src/shims/unix/fd.rs b/src/shims/unix/fd.rs index e536b78d1c..a53cd607ef 100644 --- a/src/shims/unix/fd.rs +++ b/src/shims/unix/fd.rs @@ -312,7 +312,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`fcntl`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -394,7 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(read_bytes) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } diff --git a/src/shims/unix/fs.rs b/src/shims/unix/fs.rs index 57ae209416..eb241556a5 100644 --- a/src/shims/unix/fs.rs +++ b/src/shims/unix/fs.rs @@ -378,7 +378,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`open`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -434,7 +434,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`unlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -465,7 +465,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`symlink`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -766,7 +766,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rename`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -794,7 +794,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`mkdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -822,7 +822,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // Reject if isolation is enabled. if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`rmdir`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(-1); } @@ -859,7 +859,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_target_usize(id, this)) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(Scalar::null_ptr(this)) } } @@ -947,7 +947,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None } Some(Err(e)) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; None } }; @@ -1299,7 +1299,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(path_bytes.len().try_into().unwrap()) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(-1) } } @@ -1382,7 +1382,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(Scalar::from_maybe_pointer(dest, this)) } Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(Scalar::from_target_usize(0, this)) } } @@ -1503,7 +1503,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => { // "On error, -1 is returned, and errno is set to // indicate the error" - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; return Ok(-1); } }, @@ -1582,7 +1582,7 @@ impl FileMetadata { let metadata = match metadata { Ok(metadata) => metadata, Err(e) => { - ecx.set_last_error_from_io_error(e.kind())?; + ecx.set_last_error_from_io_error(e)?; return Ok(None); } }; diff --git a/src/shims/windows/env.rs b/src/shims/windows/env.rs index b3bc5d4d85..0e52959b76 100644 --- a/src/shims/windows/env.rs +++ b/src/shims/windows/env.rs @@ -153,7 +153,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`GetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(Scalar::from_u32(0)); } @@ -166,7 +166,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_path_to_wide_str(&cwd, buf, size)?, ))); } - Err(e) => this.set_last_error_from_io_error(e.kind())?, + Err(e) => this.set_last_error_from_io_error(e)?, } Ok(Scalar::from_u32(0)) } @@ -185,7 +185,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op { this.reject_in_isolation("`SetCurrentDirectoryW`", reject_with)?; - this.set_last_error_from_io_error(ErrorKind::PermissionDenied)?; + this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?; return Ok(this.eval_windows("c", "FALSE")); } @@ -193,7 +193,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match env::set_current_dir(path) { Ok(()) => Ok(this.eval_windows("c", "TRUE")), Err(e) => { - this.set_last_error_from_io_error(e.kind())?; + this.set_last_error_from_io_error(e)?; Ok(this.eval_windows("c", "FALSE")) } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 28dad97477..44b0f25b55 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -225,7 +225,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let filename = this.read_path_from_wide_str(filename)?; let result = match win_absolute(&filename)? { Err(err) => { - this.set_last_error_from_io_error(err.kind())?; + this.set_last_error_from_io_error(err)?; Scalar::from_u32(0) // return zero upon failure } Ok(abs_filename) => { From 254bd48cfa7d0f6dac829bdb7c8afb79ad2fe1b2 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sun, 21 Apr 2024 21:45:18 +0200 Subject: [PATCH 185/208] Use generic `NonZero`. --- .../enum-set-discriminant-niche-variant-wrong.rs | 4 ++-- .../fail/function_pointers/abi_mismatch_repr_C.rs | 6 +++--- .../function_pointers/abi_mismatch_repr_C.stderr | 4 ++-- .../validity/cast_fn_ptr_invalid_callee_ret.rs | 12 ++++++------ .../validity/cast_fn_ptr_invalid_caller_arg.rs | 14 +++++++------- tests/pass/function_calls/abi_compat.rs | 8 ++++---- .../shims/available-parallelism-miri-num-cpus.rs | 4 ++-- tests/ui.rs | 4 ++-- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/fail/enum-set-discriminant-niche-variant-wrong.rs b/tests/fail/enum-set-discriminant-niche-variant-wrong.rs index 428f371ca5..eca6d908b4 100644 --- a/tests/fail/enum-set-discriminant-niche-variant-wrong.rs +++ b/tests/fail/enum-set-discriminant-niche-variant-wrong.rs @@ -2,7 +2,7 @@ #![feature(custom_mir)] use std::intrinsics::mir::*; -use std::num::NonZeroI32; +use std::num::NonZero; // We define our own option type so that we can control the variant indices. #[allow(unused)] @@ -13,7 +13,7 @@ enum Option { use Option::*; #[custom_mir(dialect = "runtime", phase = "optimized")] -fn set_discriminant(ptr: &mut Option) { +fn set_discriminant(ptr: &mut Option>) { mir! { { // We set the discriminant to `Some`, which is a NOP since this is the niched variant. diff --git a/tests/fail/function_pointers/abi_mismatch_repr_C.rs b/tests/fail/function_pointers/abi_mismatch_repr_C.rs index 2cf4e04477..c5900489b4 100644 --- a/tests/fail/function_pointers/abi_mismatch_repr_C.rs +++ b/tests/fail/function_pointers/abi_mismatch_repr_C.rs @@ -1,7 +1,7 @@ -use std::num::*; +use std::num::NonZero; #[repr(C)] -struct S1(NonZeroI32); +struct S1(NonZero); #[repr(C)] struct S2(i32); @@ -11,6 +11,6 @@ fn callee(_s: S2) {} fn main() { let fnptr: fn(S2) = callee; let fnptr: fn(S1) = unsafe { std::mem::transmute(fnptr) }; - fnptr(S1(NonZeroI32::new(1).unwrap())); + fnptr(S1(NonZero::new(1).unwrap())); //~^ ERROR: calling a function with argument of type S2 passing data of type S1 } diff --git a/tests/fail/function_pointers/abi_mismatch_repr_C.stderr b/tests/fail/function_pointers/abi_mismatch_repr_C.stderr index eaacc32bf6..8ec19db813 100644 --- a/tests/fail/function_pointers/abi_mismatch_repr_C.stderr +++ b/tests/fail/function_pointers/abi_mismatch_repr_C.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: calling a function with argument of type S2 passing data of type S1 --> $DIR/abi_mismatch_repr_C.rs:LL:CC | -LL | fnptr(S1(NonZeroI32::new(1).unwrap())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with argument of type S2 passing data of type S1 +LL | fnptr(S1(NonZero::new(1).unwrap())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with argument of type S2 passing data of type S1 | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs b/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs index 7cdc15c609..9fabdd8e86 100644 --- a/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs +++ b/tests/fail/validity/cast_fn_ptr_invalid_callee_ret.rs @@ -2,15 +2,15 @@ #![feature(core_intrinsics, custom_mir)] use std::intrinsics::mir::*; -use std::num::NonZeroU32; +use std::num::NonZero; use std::ptr; -// This function supposedly returns a NonZeroU32, but actually returns something invalid in a way that -// never materializes a bad NonZeroU32 value: we take a pointer to the return place and cast the pointer +// This function supposedly returns a `NonZero`, but actually returns something invalid in a way that +// never materializes a bad `NonZero` value: we take a pointer to the return place and cast the pointer // type. That way we never get an "invalid value constructed" error inside the function, it can // only possibly be detected when the return value is passed to the caller. #[custom_mir(dialect = "runtime", phase = "optimized")] -fn f() -> NonZeroU32 { +fn f() -> NonZero { mir! { { let tmp = ptr::addr_of_mut!(RET); @@ -22,7 +22,7 @@ fn f() -> NonZeroU32 { } fn main() { - let f: fn() -> u32 = unsafe { std::mem::transmute(f as fn() -> NonZeroU32) }; - // There's a NonZeroU32-to-u32 transmute happening here + let f: fn() -> u32 = unsafe { std::mem::transmute(f as fn() -> NonZero) }; + // There's a `NonZero` to `u32` transmute happening here. f(); //~ERROR: expected something greater or equal to 1 } diff --git a/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs b/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs index 3a87bb7867..7ca26bffc1 100644 --- a/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs +++ b/tests/fail/validity/cast_fn_ptr_invalid_caller_arg.rs @@ -2,24 +2,24 @@ #![feature(core_intrinsics, custom_mir)] use std::intrinsics::mir::*; -use std::num::NonZeroU32; +use std::num::NonZero; use std::ptr; fn f(c: u32) { println!("{c}"); } -// Call that function in a bad way, with an invalid NonZeroU32, but without -// ever materializing this as a NonZeroU32 value outside the call itself. +// Call that function in a bad way, with an invalid `NonZero`, but without +// ever materializing this as a `NonZero` value outside the call itself. #[custom_mir(dialect = "runtime", phase = "optimized")] -fn call(f: fn(NonZeroU32)) { +fn call(f: fn(NonZero)) { mir! { let _res: (); { let c = 0; let tmp = ptr::addr_of!(c); - let ptr = tmp as *const NonZeroU32; - // The call site now is a NonZeroU32-to-u32 transmute. + let ptr = tmp as *const NonZero; + // The call site now is a `NonZero` to `u32` transmute. Call(_res = f(*ptr), ReturnTo(retblock), UnwindContinue()) //~ERROR: expected something greater or equal to 1 } retblock = { @@ -29,6 +29,6 @@ fn call(f: fn(NonZeroU32)) { } fn main() { - let f: fn(NonZeroU32) = unsafe { std::mem::transmute(f as fn(u32)) }; + let f: fn(NonZero) = unsafe { std::mem::transmute(f as fn(u32)) }; call(f); } diff --git a/tests/pass/function_calls/abi_compat.rs b/tests/pass/function_calls/abi_compat.rs index 14fd2d333d..136660a305 100644 --- a/tests/pass/function_calls/abi_compat.rs +++ b/tests/pass/function_calls/abi_compat.rs @@ -70,7 +70,7 @@ fn main() { test_abi_compat(0usize, 0u64); test_abi_compat(0isize, 0i64); } - test_abi_compat(42u32, num::NonZeroU32::new(1).unwrap()); + test_abi_compat(42u32, num::NonZero::new(1u32).unwrap()); // - `char` and `u32`. test_abi_compat(42u32, 'x'); // - Reference/pointer types with the same pointee. @@ -86,9 +86,9 @@ fn main() { // - Guaranteed null-pointer-optimizations (RFC 3391). test_abi_compat(&0u32 as *const u32, Some(&0u32)); test_abi_compat(main as fn(), Some(main as fn())); - test_abi_compat(0u32, Some(num::NonZeroU32::new(1).unwrap())); + test_abi_compat(0u32, Some(num::NonZero::new(1u32).unwrap())); test_abi_compat(&0u32 as *const u32, Some(Wrapper(&0u32))); - test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1).unwrap()))); + test_abi_compat(0u32, Some(Wrapper(num::NonZero::new(1u32).unwrap()))); // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible // with the wrapped field. @@ -102,7 +102,7 @@ fn main() { test_abi_newtype::<[u32; 2]>(); test_abi_newtype::<[u32; 32]>(); test_abi_newtype::>(); - test_abi_newtype::>(); + test_abi_newtype::>>(); // Extra test for assumptions made by arbitrary-self-dyn-receivers. // This is interesting since these types are not `repr(transparent)`. So this is not part of our diff --git a/tests/pass/shims/available-parallelism-miri-num-cpus.rs b/tests/pass/shims/available-parallelism-miri-num-cpus.rs index 137fa51024..0d96bc2f5e 100644 --- a/tests/pass/shims/available-parallelism-miri-num-cpus.rs +++ b/tests/pass/shims/available-parallelism-miri-num-cpus.rs @@ -1,8 +1,8 @@ //@compile-flags: -Zmiri-num-cpus=1024 -use std::num::NonZeroUsize; +use std::num::NonZero; use std::thread::available_parallelism; fn main() { - assert_eq!(available_parallelism().unwrap(), NonZeroUsize::new(1024).unwrap()); + assert_eq!(available_parallelism().unwrap(), NonZero::new(1024).unwrap()); } diff --git a/tests/ui.rs b/tests/ui.rs index efeefbe29f..c9d53927b9 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -1,5 +1,5 @@ use std::ffi::OsString; -use std::num::NonZeroUsize; +use std::num::NonZero; use std::path::{Path, PathBuf}; use std::sync::OnceLock; use std::{env, process::Command}; @@ -76,7 +76,7 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> edition: Some("2021".into()), // keep in sync with `./miri run` threads: std::env::var("MIRI_TEST_THREADS") .ok() - .map(|threads| NonZeroUsize::new(threads.parse().unwrap()).unwrap()), + .map(|threads| NonZero::new(threads.parse().unwrap()).unwrap()), ..Config::rustc(path) }; From 3a2524ab572ce9e3dadebe9a05d13dbc69120453 Mon Sep 17 00:00:00 2001 From: tiif Date: Thu, 9 May 2024 04:34:32 +0800 Subject: [PATCH 186/208] Implement non-null pointer for malloc(0) --- src/shims/alloc.rs | 27 +++++++++---------- .../fail-dep/libc/malloc_zero_double_free.rs | 7 +++++ .../libc/malloc_zero_double_free.stderr | 25 +++++++++++++++++ .../fail-dep/libc/malloc_zero_memory_leak.rs | 5 ++++ .../libc/malloc_zero_memory_leak.stderr | 15 +++++++++++ tests/pass-dep/libc/libc-mem.rs | 14 ++++++---- 6 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 tests/fail-dep/libc/malloc_zero_double_free.rs create mode 100644 tests/fail-dep/libc/malloc_zero_double_free.stderr create mode 100644 tests/fail-dep/libc/malloc_zero_memory_leak.rs create mode 100644 tests/fail-dep/libc/malloc_zero_memory_leak.stderr diff --git a/src/shims/alloc.rs b/src/shims/alloc.rs index 61e639f76e..4eefb8b439 100644 --- a/src/shims/alloc.rs +++ b/src/shims/alloc.rs @@ -36,6 +36,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { if kind == MiriMemoryKind::WinHeap || size >= min_align { return Align::from_bytes(min_align).unwrap(); } + if size == 0 { + return Align::ONE; + } // We have `size < min_align`. Round `size` *down* to the next power of two and use that. fn prev_power_of_two(x: u64) -> u64 { let next_pow2 = x.next_power_of_two(); @@ -85,21 +88,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { kind: MiriMemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); - if size == 0 { - Ok(Pointer::null()) - } else { - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } - Ok(ptr.into()) + let align = this.min_align(size, kind); + let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; + if zero_init { + // We just allocated this, the access is definitely in-bounds and fits into our address space. + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + ) + .unwrap(); } + Ok(ptr.into()) } fn free( diff --git a/tests/fail-dep/libc/malloc_zero_double_free.rs b/tests/fail-dep/libc/malloc_zero_double_free.rs new file mode 100644 index 0000000000..3298d61c8e --- /dev/null +++ b/tests/fail-dep/libc/malloc_zero_double_free.rs @@ -0,0 +1,7 @@ +fn main() { + unsafe { + let ptr = libc::malloc(0); + libc::free(ptr); + libc::free(ptr); //~ERROR: dangling + } +} diff --git a/tests/fail-dep/libc/malloc_zero_double_free.stderr b/tests/fail-dep/libc/malloc_zero_double_free.stderr new file mode 100644 index 0000000000..6437c9dbeb --- /dev/null +++ b/tests/fail-dep/libc/malloc_zero_double_free.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information +help: ALLOC was allocated here: + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | let ptr = libc::malloc(0); + | ^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/malloc_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/malloc_zero_double_free.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/fail-dep/libc/malloc_zero_memory_leak.rs b/tests/fail-dep/libc/malloc_zero_memory_leak.rs new file mode 100644 index 0000000000..b09a74f41d --- /dev/null +++ b/tests/fail-dep/libc/malloc_zero_memory_leak.rs @@ -0,0 +1,5 @@ +fn main() { + unsafe { + let _ptr = libc::malloc(0); //~ERROR: memory leak + } +} diff --git a/tests/fail-dep/libc/malloc_zero_memory_leak.stderr b/tests/fail-dep/libc/malloc_zero_memory_leak.stderr new file mode 100644 index 0000000000..65ce0dcdcd --- /dev/null +++ b/tests/fail-dep/libc/malloc_zero_memory_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 1), allocated here: + --> $DIR/malloc_zero_memory_leak.rs:LL:CC + | +LL | let _ptr = libc::malloc(0); + | ^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/malloc_zero_memory_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + diff --git a/tests/pass-dep/libc/libc-mem.rs b/tests/pass-dep/libc/libc-mem.rs index 33cdc18b70..5df3ace749 100644 --- a/tests/pass-dep/libc/libc-mem.rs +++ b/tests/pass-dep/libc/libc-mem.rs @@ -110,9 +110,10 @@ fn test_malloc() { } unsafe { - // Realloc with size 0 is okay for the null pointer + // Realloc with size 0 is okay for the null pointer (and acts like `malloc(0)`) let p2 = libc::realloc(ptr::null_mut(), 0); - assert!(p2.is_null()); + assert!(!p2.is_null()); + libc::free(p2); } unsafe { @@ -126,13 +127,16 @@ fn test_malloc() { fn test_calloc() { unsafe { let p1 = libc::calloc(0, 0); - assert!(p1.is_null()); + assert!(!p1.is_null()); + libc::free(p1); let p2 = libc::calloc(20, 0); - assert!(p2.is_null()); + assert!(!p2.is_null()); + libc::free(p2); let p3 = libc::calloc(0, 20); - assert!(p3.is_null()); + assert!(!p3.is_null()); + libc::free(p3); let p4 = libc::calloc(4, 8); assert!(!p4.is_null()); From 7e99400d8a1bbd2e1c5a643a337104c7a27eb631 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Wed, 8 May 2024 14:43:33 -0400 Subject: [PATCH 187/208] Allow test targets to be set via CLI args --- miri-script/src/commands.rs | 57 ++++++++++++++++++++++++++----------- miri-script/src/main.rs | 41 ++++++++++++++++++++++---- 2 files changed, 75 insertions(+), 23 deletions(-) diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 6d07455c0d..968dc62cd9 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -23,7 +23,9 @@ const JOSH_PORT: &str = "42042"; impl MiriEnv { /// Returns the location of the sysroot. - fn build_miri_sysroot(&mut self, quiet: bool) -> Result { + /// + /// If the target is None the sysroot will be built for the host machine. + fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&str>) -> Result { if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { // Sysroot already set, use that. return Ok(miri_sysroot.into()); @@ -35,26 +37,27 @@ impl MiriEnv { self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?; self.build(&manifest_path, &[], quiet)?; - let target = &match self.sh.var("MIRI_TEST_TARGET") { - Ok(target) => vec!["--target".into(), target], - Err(_) => vec![], - }; + let target_flag = + &if let Some(target) = target { vec!["--target", target] } else { vec![] }; + if !quiet { - match self.sh.var("MIRI_TEST_TARGET") { - Ok(target) => eprintln!("$ (building Miri sysroot for {target})"), - Err(_) => eprintln!("$ (building Miri sysroot)"), + if let Some(target) = target { + eprintln!("$ (building Miri sysroot for {target})"); + } else { + eprintln!("$ (building Miri sysroot)"); } } + let output = cmd!(self.sh, "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} -- - miri setup --print-sysroot {target...}" + miri setup --print-sysroot {target_flag...}" ).read(); let Ok(output) = output else { // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error. cmd!( self.sh, "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} -- - miri setup {target...}" + miri setup {target_flag...}" ) .run() .with_context(|| "`cargo miri setup` failed")?; @@ -161,7 +164,7 @@ impl Command { Command::Install { flags } => Self::install(flags), Command::Build { flags } => Self::build(flags), Command::Check { flags } => Self::check(flags), - Command::Test { bless, flags } => Self::test(bless, flags), + Command::Test { bless, flags, target } => Self::test(bless, flags, target), Command::Run { dep, verbose, many_seeds, flags } => Self::run(dep, verbose, many_seeds, flags), Command::Fmt { flags } => Self::fmt(flags), @@ -446,16 +449,23 @@ impl Command { Ok(()) } - fn test(bless: bool, flags: Vec) -> Result<()> { + fn test(bless: bool, flags: Vec, target: Option) -> Result<()> { let mut e = MiriEnv::new()?; + + if let Some(target) = target.as_deref() { + // Tell the sysroot which target to test. + e.sh.set_var("MIRI_TEST_TARGET", target); + } + // Prepare a sysroot. - e.build_miri_sysroot(/* quiet */ false)?; + e.build_miri_sysroot(/* quiet */ false, target.as_deref())?; // Then test, and let caller control flags. // Only in root project as `cargo-miri` has no tests. if bless { e.sh.set_var("RUSTC_BLESS", "Gesundheit"); } + e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?; Ok(()) } @@ -476,14 +486,26 @@ impl Command { .take_while(|arg| *arg != "--") .tuple_windows() .find(|(first, _)| *first == "--target"); - if let Some((_, target)) = target { + + let target_triple = if let Some((_, target)) = target { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); + + let triple = target + .clone() + .into_string() + .map_err(|_| anyhow!("invalid target triple encoding"))?; + Some(triple) } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { // Convert `MIRI_TEST_TARGET` into `--target`. flags.push("--target".into()); - flags.push(target.into()); - } + flags.push(target.clone().into()); + + Some(target) + } else { + None + }; + // Scan for "--edition", set one ourselves if that flag is not present. let have_edition = flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition"); @@ -492,7 +514,8 @@ impl Command { } // Prepare a sysroot, and add it to the flags. - let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose)?; + let miri_sysroot = + e.build_miri_sysroot(/* quiet */ !verbose, target_triple.as_deref())?; flags.push("--sysroot".into()); flags.push(miri_sysroot.into()); diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index f0ebbc8469..c92513a0fa 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -33,6 +33,9 @@ pub enum Command { bless: bool, /// Flags that are passed through to `cargo test`. flags: Vec, + /// The cross-interpretation target. + /// If none then the host is the target. + target: Option, }, /// Build miri, set up a sysroot and then run the driver with the given . /// (Also respects MIRIFLAGS environment variable.) @@ -84,9 +87,9 @@ Just build miri. are passed to `cargo build`. ./miri check : Just check miri. are passed to `cargo check`. -./miri test [--bless] : +./miri test [--bless] [--target] : Build miri, set up a sysroot and then run the test suite. are passed -to the final `cargo test` invocation. +to the test harness. ./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] : Build miri, set up a sysroot and then run the driver with the given . @@ -147,12 +150,38 @@ fn main() -> Result<()> { Some("build") => Command::Build { flags: args.collect() }, Some("check") => Command::Check { flags: args.collect() }, Some("test") => { - let bless = args.peek().is_some_and(|a| a.to_str() == Some("--bless")); - if bless { - // Consume the flag. + let mut target = std::env::var("MIRI_TEST_TARGET").ok(); + let mut bless = false; + + while let Some(arg) = args.peek().and_then(|s| s.to_str()) { + match arg { + "--bless" => bless = true, + "--target" => { + // Skip "--target" + args.next().unwrap(); + + // Check that there is a target triple, and that it is unicode. + target = if let Some(value) = args.peek() { + let target_str = value + .clone() + .into_string() + .map_err(|_| anyhow!("invalid target triple encoding"))?; + Some(target_str) + } else { + bail!("no target triple found") + } + } + // Only parse the leading flags. + _ => break, + } + + // Consume the flag, look at the next one. args.next().unwrap(); } - Command::Test { bless, flags: args.collect() } + + // Prepend a "--" so that the rest of the arguments are passed to the test driver. + let args = std::iter::once(OsString::from("--")).chain(args); + Command::Test { bless, flags: args.collect(), target } } Some("run") => { let mut dep = false; From 92b491ce2a52541489d81a9d332186d03c64a8a6 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Wed, 8 May 2024 14:44:06 -0400 Subject: [PATCH 188/208] Update CI script for the miri-script test changes --- ci/ci.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index c28a6f183b..18f1bf18c7 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -59,7 +59,7 @@ function run_tests { # them. Also error locations change so we don't run the failing tests. # We explicitly enable debug-assertions here, they are disabled by -O but we have tests # which exist to check that we panic on debug assertion failures. - time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test -- tests/{pass,panic} + time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test tests/{pass,panic} fi if [ -n "${MANY_SEEDS-}" ]; then # Also run some many-seeds tests. @@ -107,7 +107,7 @@ function run_tests_minimal { exit 1 fi - time ./miri test -- "$@" + time ./miri test "$@" # Ensure that a small smoke test of cargo-miri works. time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml --target ${MIRI_TEST_TARGET-$HOST_TARGET} From 40ad36888cf7670aa8be2ee248674786bc37b0ca Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Wed, 8 May 2024 16:37:48 -0400 Subject: [PATCH 189/208] Update documentation for miri-script test changes --- CONTRIBUTING.md | 9 ++++----- README.md | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60bc1d5282..39aed51f5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,14 +72,13 @@ For example: You can (cross-)run the entire test suite using: -``` -./miri test -MIRI_TEST_TARGET=i686-unknown-linux-gnu ./miri test +```sh +./miri test --target i686-unknown-linux-gnu ``` `./miri test FILTER` only runs those tests that contain `FILTER` in their filename (including the -base directory, e.g. `./miri test fail` will run all compile-fail tests). These filters are passed -to `cargo test`, so for multiple filters you need to use `./miri test -- FILTER1 FILTER2`. +base directory, e.g. `./miri test fail` will run all compile-fail tests). Multiple filters +are supported: `./miri test FILTER1 FILTER2`. #### Fine grained logging diff --git a/README.md b/README.md index 2c76749fbc..6e0c96499e 100644 --- a/README.md +++ b/README.md @@ -464,7 +464,7 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): setup -- only set this if you do not want to use the automatically created sysroot. When invoking `cargo miri setup`, this indicates where the sysroot will be put. * `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target - architecture to test against. `miri` and `cargo miri` accept the `--target` flag for the same + architecture to test against. The `--target` flag may be used for the same purpose. * `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests. By default, the number of cores is used. From 07f5f8bd59234fbe78f5fdeaa654ab7785a2122f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 09:18:14 +0200 Subject: [PATCH 190/208] minor tweaks --- CONTRIBUTING.md | 1 + miri-script/src/commands.rs | 6 ++---- miri-script/src/main.rs | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 39aed51f5d..57682e60c3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,6 +73,7 @@ For example: You can (cross-)run the entire test suite using: ```sh +./miri test ./miri test --target i686-unknown-linux-gnu ``` diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 968dc62cd9..43abf180d3 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -491,10 +491,8 @@ impl Command { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); - let triple = target - .clone() - .into_string() - .map_err(|_| anyhow!("invalid target triple encoding"))?; + let triple = + target.clone().into_string().map_err(|_| anyhow!("target triple is not UTF-8"))?; Some(triple) } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { // Convert `MIRI_TEST_TARGET` into `--target`. diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index c92513a0fa..c19c4c91c6 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -87,7 +87,7 @@ Just build miri. are passed to `cargo build`. ./miri check : Just check miri. are passed to `cargo check`. -./miri test [--bless] [--target] : +./miri test [--bless] [--target ] : Build miri, set up a sysroot and then run the test suite. are passed to the test harness. @@ -165,7 +165,7 @@ fn main() -> Result<()> { let target_str = value .clone() .into_string() - .map_err(|_| anyhow!("invalid target triple encoding"))?; + .map_err(|_| anyhow!("target triple is not UTF-8"))?; Some(target_str) } else { bail!("no target triple found") From fa84aeba03d7698c60b856a68dc24b989add9ca7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 11:13:54 +0200 Subject: [PATCH 191/208] make MIRI_TEST_TARGET entirely an internal thing --- CONTRIBUTING.md | 45 +++++++++++++++++++--- README.md | 34 ---------------- ci/ci.sh | 67 +++++++++++++++++--------------- miri-script/src/commands.rs | 77 +++++++++++++++---------------------- miri-script/src/main.rs | 57 +++++++++++++++++---------- miri-script/src/util.rs | 24 ++++++++++++ test-cargo-miri/run-test.py | 14 +++++-- 7 files changed, 175 insertions(+), 143 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 57682e60c3..5a747e7935 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,8 +78,8 @@ You can (cross-)run the entire test suite using: ``` `./miri test FILTER` only runs those tests that contain `FILTER` in their filename (including the -base directory, e.g. `./miri test fail` will run all compile-fail tests). Multiple filters -are supported: `./miri test FILTER1 FILTER2`. +base directory, e.g. `./miri test fail` will run all compile-fail tests). Multiple filters are +supported: `./miri test FILTER1 FILTER2` runs all tests that contain either string. #### Fine grained logging @@ -139,9 +139,8 @@ and then you can use it as if it was installed by `rustup` as a component of the in the `miri` toolchain's sysroot to prevent conflicts with other toolchains. The Miri binaries in the `cargo` bin directory (usually `~/.cargo/bin`) are managed by rustup. -There's a test for the cargo wrapper in the `test-cargo-miri` directory; run -`./run-test.py` in there to execute it. Like `./miri test`, this respects the -`MIRI_TEST_TARGET` environment variable to execute the test for another target. +There's a test for the cargo wrapper in the `test-cargo-miri` directory; run `./run-test.py` in +there to execute it. You can pass `--target` to execute the test for another target. ### Using a modified standard library @@ -287,3 +286,39 @@ https. Add the following to your `.gitconfig`: [url "git@github.com:"] pushInsteadOf = https://github.com/ ``` + +## Internal environment variables + +The following environment variables are *internal* and must not be used by +anyone but Miri itself. They are used to communicate between different Miri +binaries, and as such worth documenting: + +* `CARGO_EXTRA_FLAGS` is understood by `./miri` and passed to all host cargo invocations. +* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to + actually not interpret the code but compile it like rustc would. With `target`, Miri sets + some compiler flags to prepare the code for interpretation; with `host`, this is not done. + This environment variable is useful to be sure that the compiled `rlib`s are compatible + with Miri. +* `MIRI_CALLED_FROM_SETUP` is set during the Miri sysroot build, + which will re-invoke `cargo-miri` as the `rustc` to use for this build. +* `MIRI_CALLED_FROM_RUSTDOC` when set to any value tells `cargo-miri` that it is + running as a child process of `rustdoc`, which invokes it twice for each doc-test + and requires special treatment, most notably a check-only build before interpretation. + This is set by `cargo-miri` itself when running as a `rustdoc`-wrapper. +* `MIRI_CWD` when set to any value tells the Miri driver to change to the given + directory after loading all the source files, but before commencing + interpretation. This is useful if the interpreted program wants a different + working directory at run-time than at build-time. +* `MIRI_LOCAL_CRATES` is set by `cargo-miri` to tell the Miri driver which + crates should be given special treatment in diagnostics, in addition to the + crate currently being compiled. +* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the + value of `RUSTDOC` from before it was overwritten. +* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap + run the standard library tests in Miri. +* `MIRI_TEST_TARGET` is set by `./miri test` (and `./x.py test miri`) to tell the test harness about + the chosen target. +* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to + perform verbose logging. +* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* + operations. diff --git a/README.md b/README.md index 6e0c96499e..1bdfc4ac1a 100644 --- a/README.md +++ b/README.md @@ -463,9 +463,6 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `MIRI_SYSROOT` indicates the sysroot to use. When using `cargo miri`, this skips the automatic setup -- only set this if you do not want to use the automatically created sysroot. When invoking `cargo miri setup`, this indicates where the sysroot will be put. -* `MIRI_TEST_TARGET` (recognized by `./miri {test,run}`) indicates which target - architecture to test against. The `--target` flag may be used for the same - purpose. * `MIRI_TEST_THREADS` (recognized by `./miri test`): set the number of threads to use for running tests. By default, the number of cores is used. * `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing @@ -476,37 +473,6 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the `stderr` or `stdout` files match the actual output. -The following environment variables are *internal* and must not be used by -anyone but Miri itself. They are used to communicate between different Miri -binaries, and as such worth documenting: - -* `MIRI_BE_RUSTC` can be set to `host` or `target`. It tells the Miri driver to - actually not interpret the code but compile it like rustc would. With `target`, Miri sets - some compiler flags to prepare the code for interpretation; with `host`, this is not done. - This environment variable is useful to be sure that the compiled `rlib`s are compatible - with Miri. -* `MIRI_CALLED_FROM_SETUP` is set during the Miri sysroot build, - which will re-invoke `cargo-miri` as the `rustc` to use for this build. -* `MIRI_CALLED_FROM_RUSTDOC` when set to any value tells `cargo-miri` that it is - running as a child process of `rustdoc`, which invokes it twice for each doc-test - and requires special treatment, most notably a check-only build before interpretation. - This is set by `cargo-miri` itself when running as a `rustdoc`-wrapper. -* `MIRI_CWD` when set to any value tells the Miri driver to change to the given - directory after loading all the source files, but before commencing - interpretation. This is useful if the interpreted program wants a different - working directory at run-time than at build-time. -* `MIRI_LOCAL_CRATES` is set by `cargo-miri` to tell the Miri driver which - crates should be given special treatment in diagnostics, in addition to the - crate currently being compiled. -* `MIRI_ORIG_RUSTDOC` is set and read by different phases of `cargo-miri` to remember the - value of `RUSTDOC` from before it was overwritten. -* `MIRI_REPLACE_LIBRS_IF_NOT_TEST` when set to any value enables a hack that helps bootstrap - run the standard library tests in Miri. -* `MIRI_VERBOSE` when set to any value tells the various `cargo-miri` phases to - perform verbose logging. -* `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* - operations. - [testing-miri]: CONTRIBUTING.md#testing-the-miri-driver ## Miri `extern` functions diff --git a/ci/ci.sh b/ci/ci.sh index 18f1bf18c7..f5fbb05d89 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -31,24 +31,26 @@ time ./miri build --all-targets # the build that all the `./miri test` below wil endgroup # Run tests. Recognizes these variables: -# - MIRI_TEST_TARGET: the target to test. Empty for host target. +# - TEST_TARGET: the target to test. Empty for host target. # - GC_STRESS: if non-empty, run the GC stress test for the main test suite. # - MIR_OPT: if non-empty, re-run test `pass` tests with mir-opt-level=4 # - MANY_SEEDS: if set to N, run the "many-seeds" tests N times # - TEST_BENCH: if non-empty, check that the benchmarks all build # - CARGO_MIRI_ENV: if non-empty, set some env vars and config to potentially confuse cargo-miri function run_tests { - if [ -n "${MIRI_TEST_TARGET-}" ]; then - begingroup "Testing foreign architecture $MIRI_TEST_TARGET" + if [ -n "${TEST_TARGET-}" ]; then + begingroup "Testing foreign architecture $TEST_TARGET" + TARGET_FLAG="--target $TEST_TARGET" else begingroup "Testing host architecture" + TARGET_FLAG="" fi ## ui test suite if [ -n "${GC_STRESS-}" ]; then - time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test + time MIRIFLAGS="${MIRIFLAGS-} -Zmiri-provenance-gc=1" ./miri test $TARGET_FLAG else - time ./miri test + time ./miri test $TARGET_FLAG fi ## advanced tests @@ -59,17 +61,17 @@ function run_tests { # them. Also error locations change so we don't run the failing tests. # We explicitly enable debug-assertions here, they are disabled by -O but we have tests # which exist to check that we panic on debug assertion failures. - time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test tests/{pass,panic} + time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test $TARGET_FLAG tests/{pass,panic} fi if [ -n "${MANY_SEEDS-}" ]; then # Also run some many-seeds tests. time for FILE in tests/many-seeds/*.rs; do - ./miri run "--many-seeds=0..$MANY_SEEDS" "$FILE" + ./miri run "--many-seeds=0..$MANY_SEEDS" $TARGET_FLAG "$FILE" done fi if [ -n "${TEST_BENCH-}" ]; then # Check that the benchmarks build and run, but only once. - time HYPERFINE="hyperfine -w0 -r1" ./miri bench + time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG fi ## test-cargo-miri @@ -91,7 +93,7 @@ function run_tests { echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml fi # Run the actual test - time ${PYTHON} test-cargo-miri/run-test.py + time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG # Clean up unset RUSTC MIRI rm -rf .cargo @@ -100,17 +102,18 @@ function run_tests { } function run_tests_minimal { - if [ -n "${MIRI_TEST_TARGET-}" ]; then - begingroup "Testing MINIMAL foreign architecture $MIRI_TEST_TARGET: only testing $@" + if [ -n "${TEST_TARGET-}" ]; then + begingroup "Testing MINIMAL foreign architecture $TEST_TARGET: only testing $@" + TARGET_FLAG="--target $TEST_TARGET" else - echo "run_tests_minimal requires MIRI_TEST_TARGET to be set" + echo "run_tests_minimal requires TEST_TARGET to be set" exit 1 fi - time ./miri test "$@" + time ./miri test $TARGET_FLAG "$@" # Ensure that a small smoke test of cargo-miri works. - time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml --target ${MIRI_TEST_TARGET-$HOST_TARGET} + time cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml $TARGET_FLAG endgroup } @@ -126,33 +129,33 @@ case $HOST_TARGET in # Extra tier 1 # With reduced many-seed count to avoid spending too much time on that. # (All OSes and ABIs are run with 64 seeds at least once though via the macOS runner.) - MANY_SEEDS=16 MIRI_TEST_TARGET=i686-unknown-linux-gnu run_tests - MANY_SEEDS=16 MIRI_TEST_TARGET=aarch64-unknown-linux-gnu run_tests - MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-apple-darwin run_tests - MANY_SEEDS=16 MIRI_TEST_TARGET=x86_64-pc-windows-gnu run_tests + MANY_SEEDS=16 TEST_TARGET=i686-unknown-linux-gnu run_tests + MANY_SEEDS=16 TEST_TARGET=aarch64-unknown-linux-gnu run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-apple-darwin run_tests + MANY_SEEDS=16 TEST_TARGET=x86_64-pc-windows-gnu run_tests ;; aarch64-apple-darwin) # Host (tier 2) GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - MANY_SEEDS=64 MIRI_TEST_TARGET=i686-pc-windows-gnu run_tests - MANY_SEEDS=64 MIRI_TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests + MANY_SEEDS=64 TEST_TARGET=i686-pc-windows-gnu run_tests + MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-msvc CARGO_MIRI_ENV=1 run_tests # Extra tier 2 - MIRI_TEST_TARGET=arm-unknown-linux-gnueabi run_tests - MIRI_TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice + TEST_TARGET=arm-unknown-linux-gnueabi run_tests + TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Partially supported targets (tier 2) VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures - MIRI_TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus - MIRI_TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus - MIRI_TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random - MIRI_TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random - MIRI_TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic - MIRI_TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm - MIRI_TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm - MIRI_TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std + TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus + TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus + TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic + TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm + TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm + TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std # Custom target JSON file - MIRI_TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std + TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std ;; i686-pc-windows-msvc) # Host @@ -162,7 +165,7 @@ case $HOST_TARGET in # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, # and a 64bit target works on a 32bit host. - MIRI_TEST_TARGET=x86_64-unknown-linux-gnu run_tests + TEST_TARGET=x86_64-unknown-linux-gnu run_tests ;; *) echo "FATAL: unknown host target: $HOST_TARGET" diff --git a/miri-script/src/commands.rs b/miri-script/src/commands.rs index 43abf180d3..8e2b07ad80 100644 --- a/miri-script/src/commands.rs +++ b/miri-script/src/commands.rs @@ -1,5 +1,5 @@ use std::env; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::io::Write; use std::ops::Not; use std::ops::Range; @@ -25,7 +25,7 @@ impl MiriEnv { /// Returns the location of the sysroot. /// /// If the target is None the sysroot will be built for the host machine. - fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&str>) -> Result { + fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result { if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") { // Sysroot already set, use that. return Ok(miri_sysroot.into()); @@ -38,11 +38,12 @@ impl MiriEnv { self.build(&manifest_path, &[], quiet)?; let target_flag = - &if let Some(target) = target { vec!["--target", target] } else { vec![] }; + if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] }; + let target_flag = &target_flag; if !quiet { if let Some(target) = target { - eprintln!("$ (building Miri sysroot for {target})"); + eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy()); } else { eprintln!("$ (building Miri sysroot)"); } @@ -170,7 +171,7 @@ impl Command { Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { flags } => Self::clippy(flags), Command::Cargo { flags } => Self::cargo(flags), - Command::Bench { benches } => Self::bench(benches), + Command::Bench { target, benches } => Self::bench(target, benches), Command::Toolchain { flags } => Self::toolchain(flags), Command::RustcPull { commit } => Self::rustc_pull(commit.clone()), Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch), @@ -372,7 +373,7 @@ impl Command { Ok(()) } - fn bench(benches: Vec) -> Result<()> { + fn bench(target: Option, benches: Vec) -> Result<()> { // The hyperfine to use let hyperfine = env::var("HYPERFINE"); let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none"); @@ -380,8 +381,6 @@ impl Command { let Some((program_name, args)) = hyperfine.split_first() else { bail!("expected HYPERFINE environment variable to be non-empty"); }; - // Extra flags to pass to cargo. - let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default(); // Make sure we have an up-to-date Miri installed and selected the right toolchain. Self::install(vec![])?; @@ -397,6 +396,14 @@ impl Command { } else { benches.to_owned() }; + let target_flag = if let Some(target) = target { + let mut flag = OsString::from("--target="); + flag.push(target); + flag + } else { + OsString::new() + }; + let target_flag = &target_flag; // Run the requested benchmarks for bench in benches { let current_bench = path!(benches_dir / bench / "Cargo.toml"); @@ -404,7 +411,7 @@ impl Command { // That seems to make Windows CI happy. cmd!( sh, - "{program_name} {args...} 'cargo miri run '{cargo_extra_flags}' --manifest-path \"'{current_bench}'\"'" + "{program_name} {args...} 'cargo miri run '{target_flag}' --manifest-path \"'{current_bench}'\"'" ) .run()?; } @@ -449,23 +456,26 @@ impl Command { Ok(()) } - fn test(bless: bool, flags: Vec, target: Option) -> Result<()> { + fn test(bless: bool, mut flags: Vec, target: Option) -> Result<()> { let mut e = MiriEnv::new()?; - if let Some(target) = target.as_deref() { - // Tell the sysroot which target to test. - e.sh.set_var("MIRI_TEST_TARGET", target); - } - // Prepare a sysroot. e.build_miri_sysroot(/* quiet */ false, target.as_deref())?; - // Then test, and let caller control flags. - // Only in root project as `cargo-miri` has no tests. + // Forward information to test harness. if bless { e.sh.set_var("RUSTC_BLESS", "Gesundheit"); } + if let Some(target) = target { + // Tell the harness which target to test. + e.sh.set_var("MIRI_TEST_TARGET", target); + } + // Make sure the flags are going to the test harness, not cargo. + flags.insert(0, "--".into()); + + // Then test, and let caller control flags. + // Only in root project as `cargo-miri` has no tests. e.test(path!(e.miri_dir / "Cargo.toml"), &flags)?; Ok(()) } @@ -477,43 +487,16 @@ impl Command { mut flags: Vec, ) -> Result<()> { let mut e = MiriEnv::new()?; - // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so - // that we set the MIRI_SYSROOT up the right way. We must make sure that - // MIRI_TEST_TARGET and `--target` are in sync. - use itertools::Itertools; - let target = flags - .iter() - .take_while(|arg| *arg != "--") - .tuple_windows() - .find(|(first, _)| *first == "--target"); - - let target_triple = if let Some((_, target)) = target { - // Found it! - e.sh.set_var("MIRI_TEST_TARGET", target); - - let triple = - target.clone().into_string().map_err(|_| anyhow!("target triple is not UTF-8"))?; - Some(triple) - } else if let Ok(target) = std::env::var("MIRI_TEST_TARGET") { - // Convert `MIRI_TEST_TARGET` into `--target`. - flags.push("--target".into()); - flags.push(target.clone().into()); - - Some(target) - } else { - None - }; + let target = arg_flag_value(&flags, "--target"); // Scan for "--edition", set one ourselves if that flag is not present. - let have_edition = - flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition"); + let have_edition = arg_flag_value(&flags, "--edition").is_some(); if !have_edition { flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.` } // Prepare a sysroot, and add it to the flags. - let miri_sysroot = - e.build_miri_sysroot(/* quiet */ !verbose, target_triple.as_deref())?; + let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?; flags.push("--sysroot".into()); flags.push(miri_sysroot.into()); diff --git a/miri-script/src/main.rs b/miri-script/src/main.rs index c19c4c91c6..a8626ceb45 100644 --- a/miri-script/src/main.rs +++ b/miri-script/src/main.rs @@ -31,11 +31,11 @@ pub enum Command { /// Build miri, set up a sysroot and then run the test suite. Test { bless: bool, - /// Flags that are passed through to `cargo test`. - flags: Vec, /// The cross-interpretation target. /// If none then the host is the target. - target: Option, + target: Option, + /// Flags that are passed through to the test harness. + flags: Vec, }, /// Build miri, set up a sysroot and then run the driver with the given . /// (Also respects MIRIFLAGS environment variable.) @@ -61,6 +61,7 @@ pub enum Command { Cargo { flags: Vec }, /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. Bench { + target: Option, /// List of benchmarks to run. By default all benchmarks are run. benches: Vec, }, @@ -88,8 +89,8 @@ Just build miri. are passed to `cargo build`. Just check miri. are passed to `cargo check`. ./miri test [--bless] [--target ] : -Build miri, set up a sysroot and then run the test suite. are passed -to the test harness. +Build miri, set up a sysroot and then run the test suite. + are passed to the test harness. ./miri run [--dep] [-v|--verbose] [--many-seeds|--many-seeds=..to|--many-seeds=from..to] : Build miri, set up a sysroot and then run the driver with the given . @@ -113,7 +114,7 @@ install`. Sets up the rpath such that the installed binary should work in any working directory. Note that the binaries are placed in the `miri` toolchain sysroot, to prevent conflicts with other toolchains. -./miri bench : +./miri bench [--target ] : Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed. can explicitly list the benchmarks to run; by default, all of them are run. @@ -150,7 +151,7 @@ fn main() -> Result<()> { Some("build") => Command::Build { flags: args.collect() }, Some("check") => Command::Check { flags: args.collect() }, Some("test") => { - let mut target = std::env::var("MIRI_TEST_TARGET").ok(); + let mut target = None; let mut bless = false; while let Some(arg) = args.peek().and_then(|s| s.to_str()) { @@ -159,17 +160,11 @@ fn main() -> Result<()> { "--target" => { // Skip "--target" args.next().unwrap(); - - // Check that there is a target triple, and that it is unicode. - target = if let Some(value) = args.peek() { - let target_str = value - .clone() - .into_string() - .map_err(|_| anyhow!("target triple is not UTF-8"))?; - Some(target_str) - } else { - bail!("no target triple found") - } + // Next argument is the target triple. + let val = args.peek().ok_or_else(|| { + anyhow!("`--target` must be followed by target triple") + })?; + target = Some(val.to_owned()); } // Only parse the leading flags. _ => break, @@ -179,8 +174,6 @@ fn main() -> Result<()> { args.next().unwrap(); } - // Prepend a "--" so that the rest of the arguments are passed to the test driver. - let args = std::iter::once(OsString::from("--")).chain(args); Command::Test { bless, flags: args.collect(), target } } Some("run") => { @@ -217,7 +210,29 @@ fn main() -> Result<()> { Some("clippy") => Command::Clippy { flags: args.collect() }, Some("cargo") => Command::Cargo { flags: args.collect() }, Some("install") => Command::Install { flags: args.collect() }, - Some("bench") => Command::Bench { benches: args.collect() }, + Some("bench") => { + let mut target = None; + while let Some(arg) = args.peek().and_then(|s| s.to_str()) { + match arg { + "--target" => { + // Skip "--target" + args.next().unwrap(); + // Next argument is the target triple. + let val = args.peek().ok_or_else(|| { + anyhow!("`--target` must be followed by target triple") + })?; + target = Some(val.to_owned()); + } + // Only parse the leading flags. + _ => break, + } + + // Consume the flag, look at the next one. + args.next().unwrap(); + } + + Command::Bench { target, benches: args.collect() } + } Some("toolchain") => Command::Toolchain { flags: args.collect() }, Some("rustc-pull") => { let commit = args.next().map(|a| a.to_string_lossy().into_owned()); diff --git a/miri-script/src/util.rs b/miri-script/src/util.rs index 23b5e936ed..2b5791f3ea 100644 --- a/miri-script/src/util.rs +++ b/miri-script/src/util.rs @@ -27,6 +27,30 @@ pub fn flagsplit(flags: &str) -> Vec { flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() } +pub fn arg_flag_value( + args: impl IntoIterator>, + flag: &str, +) -> Option { + let mut args = args.into_iter(); + while let Some(arg) = args.next() { + let arg = arg.as_ref(); + if arg == "--" { + return None; + } + let Some(arg) = arg.to_str() else { + // Skip non-UTF-8 arguments. + continue; + }; + if arg == flag { + // Next one is the value. + return Some(args.next()?.as_ref().to_owned()); + } else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) { + return Some(val.to_owned().into()); + } + } + None +} + /// Some extra state we track for building Miri, such as the right RUSTFLAGS. pub struct MiriEnv { /// miri_dir is the root of the miri repository checkout we are working in. diff --git a/test-cargo-miri/run-test.py b/test-cargo-miri/run-test.py index 2639d29b73..8bab66f3af 100755 --- a/test-cargo-miri/run-test.py +++ b/test-cargo-miri/run-test.py @@ -10,6 +10,7 @@ import re import subprocess import sys +import argparse CGREEN = '\33[32m' CBOLD = '\33[1m' @@ -25,8 +26,8 @@ def cargo_miri(cmd, quiet = True): args = ["cargo", "miri", cmd] + CARGO_EXTRA_FLAGS if quiet: args += ["-q"] - if 'MIRI_TEST_TARGET' in os.environ: - args += ["--target", os.environ['MIRI_TEST_TARGET']] + if ARGS.target: + args += ["--target", ARGS.target] return args def normalize_stdout(str): @@ -133,7 +134,7 @@ def test_cargo_miri_run(): def test_cargo_miri_test(): # rustdoc is not run on foreign targets - is_foreign = 'MIRI_TEST_TARGET' in os.environ + is_foreign = ARGS.target is not None default_ref = "test.cross-target.stdout.ref" if is_foreign else "test.default.stdout.ref" filter_ref = "test.filter.cross-target.stdout.ref" if is_foreign else "test.filter.stdout.ref" @@ -182,16 +183,21 @@ def test_cargo_miri_test(): env={'MIRIFLAGS': "-Zmiri-permissive-provenance"}, ) +args_parser = argparse.ArgumentParser(description='`cargo miri` testing') +args_parser.add_argument('--target', help='the target to test') +ARGS = args_parser.parse_args() + os.chdir(os.path.dirname(os.path.realpath(__file__))) os.environ["CARGO_TARGET_DIR"] = "target" # this affects the location of the target directory that we need to check os.environ["RUST_TEST_NOCAPTURE"] = "0" # this affects test output, so make sure it is not set os.environ["RUST_TEST_THREADS"] = "1" # avoid non-deterministic output due to concurrent test runs -target_str = " for target {}".format(os.environ['MIRI_TEST_TARGET']) if 'MIRI_TEST_TARGET' in os.environ else "" +target_str = " for target {}".format(ARGS.target) if ARGS.target else "" print(CGREEN + CBOLD + "## Running `cargo miri` tests{}".format(target_str) + CEND) test_cargo_miri_run() test_cargo_miri_test() + # Ensure we did not create anything outside the expected target dir. for target_dir in ["target", "custom-run", "custom-test", "config-cli"]: if os.listdir(target_dir) != ["miri"]: From 834afd9888a8886cda34b7e9e4108c222da0805f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 11:21:10 +0200 Subject: [PATCH 192/208] make RUSTC_BLESS entirely an internal thing --- CONTRIBUTING.md | 2 ++ README.md | 2 -- test-cargo-miri/run-test.py | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a747e7935..092ad46a7c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -322,3 +322,5 @@ binaries, and as such worth documenting: perform verbose logging. * `MIRI_HOST_SYSROOT` is set by bootstrap to tell `cargo-miri` which sysroot to use for *host* operations. +* `RUSTC_BLESS` is set by `./miri test` (and `./x.py test miri`) to indicate bless-mode to the test + harness. diff --git a/README.md b/README.md index 1bdfc4ac1a..92cb7b3782 100644 --- a/README.md +++ b/README.md @@ -468,8 +468,6 @@ by all intended entry points, i.e. `cargo miri` and `./miri {test,run}`): * `MIRI_NO_STD` makes sure that the target's sysroot is built without libstd. This allows testing and running no_std programs. (Miri has a heuristic to detect no-std targets based on the target name; this environment variable is only needed when that heuristic fails.) -* `RUSTC_BLESS` (recognized by `./miri test` and `cargo-miri-test/run-test.py`): overwrite all - `stderr` and `stdout` files instead of checking whether the output matches. * `MIRI_SKIP_UI_CHECKS` (recognized by `./miri test`): don't check whether the `stderr` or `stdout` files match the actual output. diff --git a/test-cargo-miri/run-test.py b/test-cargo-miri/run-test.py index 8bab66f3af..83f3e4c919 100755 --- a/test-cargo-miri/run-test.py +++ b/test-cargo-miri/run-test.py @@ -36,7 +36,7 @@ def normalize_stdout(str): return str def check_output(actual, path, name): - if os.environ.get("RUSTC_BLESS", "0") != "0": + if ARGS.bless: # Write the output only if bless is set open(path, mode='w').write(actual) return True @@ -185,6 +185,7 @@ def test_cargo_miri_test(): args_parser = argparse.ArgumentParser(description='`cargo miri` testing') args_parser.add_argument('--target', help='the target to test') +args_parser.add_argument('--bless', help='bless the reference files', action='store_true') ARGS = args_parser.parse_args() os.chdir(os.path.dirname(os.path.realpath(__file__))) From 72bcef8fed8968dc432e36cd03404a8b4523f754 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 11:42:05 +0200 Subject: [PATCH 193/208] do not run symlink tests on Windows hosts --- tests/pass-dep/libc/libc-fs-readlink.rs | 51 +++++++++++ tests/pass-dep/libc/libc-fs-with-isolation.rs | 2 +- tests/pass-dep/libc/libc-fs.rs | 84 ++---------------- tests/pass-dep/libc/libc-time.rs | 2 +- tests/pass/shims/fs-symlink.rs | 50 +++++++++++ tests/pass/shims/fs.rs | 88 ++++--------------- tests/utils/fs.rs | 24 +++++ 7 files changed, 151 insertions(+), 150 deletions(-) create mode 100644 tests/pass-dep/libc/libc-fs-readlink.rs create mode 100644 tests/pass/shims/fs-symlink.rs diff --git a/tests/pass-dep/libc/libc-fs-readlink.rs b/tests/pass-dep/libc/libc-fs-readlink.rs new file mode 100644 index 0000000000..d72edd7d9e --- /dev/null +++ b/tests/pass-dep/libc/libc-fs-readlink.rs @@ -0,0 +1,51 @@ +// Symlink tests are separate since they don't in general work on a Windows host. +//@ignore-host-windows: creating symlinks requires admin permissions on Windows +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation + +use std::ffi::CString; +use std::io::{Error, ErrorKind}; +use std::os::unix::ffi::OsStrExt; + +#[path = "../../utils/mod.rs"] +mod utils; + +fn main() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); + let expected_path = path.as_os_str().as_bytes(); + + let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + + // Test that the expected string gets written to a buffer of proper + // length, and that a trailing null byte is not written. + let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); + let symlink_c_ptr = symlink_c_str.as_ptr(); + + // Make the buf one byte larger than it needs to be, + // and check that the last byte is not overwritten. + let mut large_buf = vec![0xFF; expected_path.len() + 1]; + let res = + unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) }; + // Check that the resolved path was properly written into the buf. + assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); + assert_eq!(large_buf.last(), Some(&0xFF)); + assert_eq!(res, large_buf.len() as isize - 1); + + // Test that the resolved path is truncated if the provided buffer + // is too small. + let mut small_buf = [0u8; 2]; + let res = + unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) }; + assert_eq!(small_buf, &expected_path[..small_buf.len()]); + assert_eq!(res, small_buf.len() as isize); + + // Test that we report a proper error for a missing path. + let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap(); + let res = unsafe { + libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) + }; + assert_eq!(res, -1); + assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); +} diff --git a/tests/pass-dep/libc/libc-fs-with-isolation.rs b/tests/pass-dep/libc/libc-fs-with-isolation.rs index 5185db0b0e..088a632427 100644 --- a/tests/pass-dep/libc/libc-fs-with-isolation.rs +++ b/tests/pass-dep/libc/libc-fs-with-isolation.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: no libc on Windows +//@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT" diff --git a/tests/pass-dep/libc/libc-fs.rs b/tests/pass-dep/libc/libc-fs.rs index 0dd849a045..80c9757e9c 100644 --- a/tests/pass-dep/libc/libc-fs.rs +++ b/tests/pass-dep/libc/libc-fs.rs @@ -1,11 +1,11 @@ -//@ignore-target-windows: no libc on Windows +//@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation #![feature(io_error_more)] #![feature(io_error_uncategorized)] use std::ffi::{CStr, CString, OsString}; -use std::fs::{canonicalize, remove_dir_all, remove_file, File}; +use std::fs::{canonicalize, remove_file, File}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; @@ -21,7 +21,6 @@ fn main() { test_ftruncate::(libc::ftruncate); #[cfg(target_os = "linux")] test_ftruncate::(libc::ftruncate64); - test_readlink(); test_file_open_unix_allow_two_args(); test_file_open_unix_needs_three_args(); test_file_open_unix_extra_third_arg(); @@ -38,33 +37,8 @@ fn main() { test_isatty(); } -/// Prepare: compute filename and make sure the file does not exist. -fn prepare(filename: &str) -> PathBuf { - let path = utils::tmp().join(filename); - // Clean the paths for robustness. - remove_file(&path).ok(); - path -} - -/// Prepare directory: compute directory name and make sure it does not exist. -#[allow(unused)] -fn prepare_dir(dirname: &str) -> PathBuf { - let path = utils::tmp().join(&dirname); - // Clean the directory for robustness. - remove_dir_all(&path).ok(); - path -} - -/// Prepare like above, and also write some initial content to the file. -fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { - let path = prepare(filename); - let mut file = File::create(&path).unwrap(); - file.write(content).unwrap(); - path -} - fn test_file_open_unix_allow_two_args() { - let path = prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); + let path = utils::prepare_with_content("test_file_open_unix_allow_two_args.txt", &[]); let mut name = path.into_os_string(); name.push("\0"); @@ -73,7 +47,7 @@ fn test_file_open_unix_allow_two_args() { } fn test_file_open_unix_needs_three_args() { - let path = prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); + let path = utils::prepare_with_content("test_file_open_unix_needs_three_args.txt", &[]); let mut name = path.into_os_string(); name.push("\0"); @@ -82,7 +56,7 @@ fn test_file_open_unix_needs_three_args() { } fn test_file_open_unix_extra_third_arg() { - let path = prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); + let path = utils::prepare_with_content("test_file_open_unix_extra_third_arg.txt", &[]); let mut name = path.into_os_string(); name.push("\0"); @@ -106,49 +80,9 @@ fn test_canonicalize_too_long() { assert!(canonicalize(too_long).is_err()); } -fn test_readlink() { - let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); - let expected_path = path.as_os_str().as_bytes(); - - let symlink_path = prepare("miri_test_fs_symlink.txt"); - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - - // Test that the expected string gets written to a buffer of proper - // length, and that a trailing null byte is not written. - let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap(); - let symlink_c_ptr = symlink_c_str.as_ptr(); - - // Make the buf one byte larger than it needs to be, - // and check that the last byte is not overwritten. - let mut large_buf = vec![0xFF; expected_path.len() + 1]; - let res = - unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) }; - // Check that the resolved path was properly written into the buf. - assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path); - assert_eq!(large_buf.last(), Some(&0xFF)); - assert_eq!(res, large_buf.len() as isize - 1); - - // Test that the resolved path is truncated if the provided buffer - // is too small. - let mut small_buf = [0u8; 2]; - let res = - unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) }; - assert_eq!(small_buf, &expected_path[..small_buf.len()]); - assert_eq!(res, small_buf.len() as isize); - - // Test that we report a proper error for a missing path. - let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap(); - let res = unsafe { - libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) - }; - assert_eq!(res, -1); - assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound); -} - fn test_rename() { - let path1 = prepare("miri_test_libc_fs_source.txt"); - let path2 = prepare("miri_test_libc_fs_rename_destination.txt"); + let path1 = utils::prepare("miri_test_libc_fs_source.txt"); + let path2 = utils::prepare("miri_test_libc_fs_rename_destination.txt"); let file = File::create(&path1).unwrap(); drop(file); @@ -178,7 +112,7 @@ fn test_ftruncate>( // https://docs.rs/libc/latest/i686-unknown-linux-gnu/libc/type.off_t.html let bytes = b"hello"; - let path = prepare("miri_test_libc_fs_ftruncate.txt"); + let path = utils::prepare("miri_test_libc_fs_ftruncate.txt"); let mut file = File::create(&path).unwrap(); file.write(bytes).unwrap(); file.sync_all().unwrap(); @@ -209,7 +143,7 @@ fn test_ftruncate>( fn test_o_tmpfile_flag() { use std::fs::{create_dir, OpenOptions}; use std::os::unix::fs::OpenOptionsExt; - let dir_path = prepare_dir("miri_test_fs_dir"); + let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); // test that the `O_TMPFILE` custom flag gracefully errors instead of stopping execution assert_eq!( diff --git a/tests/pass-dep/libc/libc-time.rs b/tests/pass-dep/libc/libc-time.rs index 69c75bd8ca..ea1e49a072 100644 --- a/tests/pass-dep/libc/libc-time.rs +++ b/tests/pass-dep/libc/libc-time.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows: no libc on Windows +//@ignore-target-windows: no libc time APIs on Windows //@compile-flags: -Zmiri-disable-isolation use std::ffi::CStr; use std::{env, mem, ptr}; diff --git a/tests/pass/shims/fs-symlink.rs b/tests/pass/shims/fs-symlink.rs new file mode 100644 index 0000000000..4d5103b24c --- /dev/null +++ b/tests/pass/shims/fs-symlink.rs @@ -0,0 +1,50 @@ +// Symlink tests are separate since they don't in general work on a Windows host. +//@ignore-host-windows: creating symlinks requires admin permissions on Windows +//@ignore-target-windows: File handling is not implemented yet +//@compile-flags: -Zmiri-disable-isolation + +use std::fs::{read_link, remove_file, File}; +use std::io::{Read, Result}; +use std::path::Path; + +#[path = "../../utils/mod.rs"] +mod utils; + +fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> { + // Test that the file metadata is correct. + let metadata = path.metadata()?; + // `path` should point to a file. + assert!(metadata.is_file()); + // The size of the file must be equal to the number of written bytes. + assert_eq!(bytes.len() as u64, metadata.len()); + Ok(()) +} + +fn main() { + let bytes = b"Hello, World!\n"; + let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes); + let symlink_path = utils::prepare("miri_test_fs_symlink.txt"); + + // Creating a symbolic link should succeed. + #[cfg(unix)] + std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); + #[cfg(windows)] + std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap(); + // Test that the symbolic link has the same contents as the file. + let mut symlink_file = File::open(&symlink_path).unwrap(); + let mut contents = Vec::new(); + symlink_file.read_to_end(&mut contents).unwrap(); + assert_eq!(bytes, contents.as_slice()); + + // Test that metadata of a symbolic link (i.e., the file it points to) is correct. + check_metadata(bytes, &symlink_path).unwrap(); + // Test that the metadata of a symbolic link is correct when not following it. + assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink()); + // Check that we can follow the link. + assert_eq!(read_link(&symlink_path).unwrap(), path); + // Removing symbolic link should succeed. + remove_file(&symlink_path).unwrap(); + + // Removing file should succeed. + remove_file(&path).unwrap(); +} diff --git a/tests/pass/shims/fs.rs b/tests/pass/shims/fs.rs index 8a500b857b..35980fad15 100644 --- a/tests/pass/shims/fs.rs +++ b/tests/pass/shims/fs.rs @@ -1,21 +1,17 @@ //@ignore-target-windows: File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation -// If this test is failing for you locally, you can try -// 1. Deleting the files `/tmp/miri_*` -// 2. Setting `MIRI_TEMP` or `TMPDIR` to a different directory, without the `miri_*` files - #![feature(io_error_more)] #![feature(io_error_uncategorized)] use std::collections::HashMap; use std::ffi::OsString; use std::fs::{ - canonicalize, create_dir, read_dir, read_link, remove_dir, remove_dir_all, remove_file, rename, - File, OpenOptions, + canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, File, + OpenOptions, }; use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; -use std::path::{Path, PathBuf}; +use std::path::Path; #[path = "../../utils/mod.rs"] mod utils; @@ -29,7 +25,6 @@ fn main() { test_metadata(); test_file_set_len(); test_file_sync(); - test_symlink(); test_errors(); test_rename(); test_directory(); @@ -37,30 +32,6 @@ fn main() { test_from_raw_os_error(); } -/// Prepare: compute filename and make sure the file does not exist. -fn prepare(filename: &str) -> PathBuf { - let path = utils::tmp().join(filename); - // Clean the paths for robustness. - remove_file(&path).ok(); - path -} - -/// Prepare directory: compute directory name and make sure it does not exist. -fn prepare_dir(dirname: &str) -> PathBuf { - let path = utils::tmp().join(&dirname); - // Clean the directory for robustness. - remove_dir_all(&path).ok(); - path -} - -/// Prepare like above, and also write some initial content to the file. -fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { - let path = prepare(filename); - let mut file = File::create(&path).unwrap(); - file.write(content).unwrap(); - path -} - fn test_path_conversion() { let tmp = utils::tmp(); assert!(tmp.is_absolute(), "{:?} is not absolute", tmp); @@ -69,7 +40,7 @@ fn test_path_conversion() { fn test_file() { let bytes = b"Hello, World!\n"; - let path = prepare("miri_test_fs_file.txt"); + let path = utils::prepare("miri_test_fs_file.txt"); // Test creating, writing and closing a file (closing is tested when `file` is dropped). let mut file = File::create(&path).unwrap(); @@ -96,7 +67,7 @@ fn test_file() { fn test_file_clone() { let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_file_clone.txt", bytes); // Cloning a file should be successful. let file = File::open(&path).unwrap(); @@ -111,7 +82,7 @@ fn test_file_clone() { } fn test_file_create_new() { - let path = prepare("miri_test_fs_file_create_new.txt"); + let path = utils::prepare("miri_test_fs_file_create_new.txt"); // Creating a new file that doesn't yet exist should succeed. OpenOptions::new().write(true).create_new(true).open(&path).unwrap(); @@ -129,7 +100,7 @@ fn test_file_create_new() { fn test_seek() { let bytes = b"Hello, entire World!\n"; - let path = prepare_with_content("miri_test_fs_seek.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_seek.txt", bytes); let mut file = File::open(&path).unwrap(); let mut contents = Vec::new(); @@ -168,7 +139,7 @@ fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> { fn test_metadata() { let bytes = b"Hello, meta-World!\n"; - let path = prepare_with_content("miri_test_fs_metadata.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_metadata.txt", bytes); // Test that metadata of an absolute path is correct. check_metadata(bytes, &path).unwrap(); @@ -182,7 +153,7 @@ fn test_metadata() { fn test_file_set_len() { let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_set_len.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_set_len.txt", bytes); // Test extending the file let mut file = OpenOptions::new().read(true).write(true).open(&path).unwrap(); @@ -208,7 +179,7 @@ fn test_file_set_len() { fn test_file_sync() { let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_sync.txt", bytes); + let path = utils::prepare_with_content("miri_test_fs_sync.txt", bytes); // Test that we can call sync_data and sync_all (can't readily test effects of this operation) let file = OpenOptions::new().write(true).open(&path).unwrap(); @@ -223,38 +194,9 @@ fn test_file_sync() { remove_file(&path).unwrap(); } -fn test_symlink() { - let bytes = b"Hello, World!\n"; - let path = prepare_with_content("miri_test_fs_link_target.txt", bytes); - let symlink_path = prepare("miri_test_fs_symlink.txt"); - - // Creating a symbolic link should succeed. - #[cfg(unix)] - std::os::unix::fs::symlink(&path, &symlink_path).unwrap(); - #[cfg(windows)] - std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap(); - // Test that the symbolic link has the same contents as the file. - let mut symlink_file = File::open(&symlink_path).unwrap(); - let mut contents = Vec::new(); - symlink_file.read_to_end(&mut contents).unwrap(); - assert_eq!(bytes, contents.as_slice()); - - // Test that metadata of a symbolic link (i.e., the file it points to) is correct. - check_metadata(bytes, &symlink_path).unwrap(); - // Test that the metadata of a symbolic link is correct when not following it. - assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink()); - // Check that we can follow the link. - assert_eq!(read_link(&symlink_path).unwrap(), path); - // Removing symbolic link should succeed. - remove_file(&symlink_path).unwrap(); - - // Removing file should succeed. - remove_file(&path).unwrap(); -} - fn test_errors() { let bytes = b"Hello, World!\n"; - let path = prepare("miri_test_fs_errors.txt"); + let path = utils::prepare("miri_test_fs_errors.txt"); // The following tests also check that the `__errno_location()` shim is working properly. // Opening a non-existing file should fail with a "not found" error. @@ -269,8 +211,8 @@ fn test_errors() { fn test_rename() { // Renaming a file should succeed. - let path1 = prepare("miri_test_fs_rename_source.txt"); - let path2 = prepare("miri_test_fs_rename_destination.txt"); + let path1 = utils::prepare("miri_test_fs_rename_source.txt"); + let path2 = utils::prepare("miri_test_fs_rename_destination.txt"); let file = File::create(&path1).unwrap(); drop(file); @@ -289,7 +231,7 @@ fn test_rename() { } fn test_canonicalize() { - let dir_path = prepare_dir("miri_test_fs_dir"); + let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); let path = dir_path.join("test_file"); drop(File::create(&path).unwrap()); @@ -301,7 +243,7 @@ fn test_canonicalize() { } fn test_directory() { - let dir_path = prepare_dir("miri_test_fs_dir"); + let dir_path = utils::prepare_dir("miri_test_fs_dir"); // Creating a directory should succeed. create_dir(&dir_path).unwrap(); // Test that the metadata of a directory is correct. diff --git a/tests/utils/fs.rs b/tests/utils/fs.rs index 0242a22806..7340908626 100644 --- a/tests/utils/fs.rs +++ b/tests/utils/fs.rs @@ -1,4 +1,5 @@ use std::ffi::OsString; +use std::fs; use std::path::PathBuf; use super::miri_extern; @@ -27,3 +28,26 @@ pub fn tmp() -> PathBuf { // These are host paths. We need to convert them to the target. host_to_target_path(path) } + +/// Prepare: compute filename and make sure the file does not exist. +pub fn prepare(filename: &str) -> PathBuf { + let path = tmp().join(filename); + // Clean the paths for robustness. + fs::remove_file(&path).ok(); + path +} + +/// Prepare like above, and also write some initial content to the file. +pub fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf { + let path = prepare(filename); + fs::write(&path, content).unwrap(); + path +} + +/// Prepare directory: compute directory name and make sure it does not exist. +pub fn prepare_dir(dirname: &str) -> PathBuf { + let path = tmp().join(&dirname); + // Clean the directory for robustness. + fs::remove_dir_all(&path).ok(); + path +} From dd3e16b0b1c80c0312f65255f23a5879941bfdf6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 9 May 2024 13:09:47 +0200 Subject: [PATCH 194/208] interpret/miri: better errors on failing offset_from --- .../ptr_offset_from_different_ints.rs | 21 +++++++++++++++++++ .../ptr_offset_from_different_ints.stderr | 15 +++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/fail/intrinsics/ptr_offset_from_different_ints.rs create mode 100644 tests/fail/intrinsics/ptr_offset_from_different_ints.stderr diff --git a/tests/fail/intrinsics/ptr_offset_from_different_ints.rs b/tests/fail/intrinsics/ptr_offset_from_different_ints.rs new file mode 100644 index 0000000000..57b4fd0ded --- /dev/null +++ b/tests/fail/intrinsics/ptr_offset_from_different_ints.rs @@ -0,0 +1,21 @@ +#![feature(strict_provenance)] +use core::ptr; + +fn main() { + unsafe { + let base = ptr::without_provenance::<()>(10); + let unit = &*base; + let p1 = unit as *const (); + + let base = ptr::without_provenance::<()>(11); + let unit = &*base; + let p2 = unit as *const (); + + // Seems to work because they are same pointer + // even though it's dangling. + let _ = p1.byte_offset_from(p1); + + // UB because different pointers. + let _ = p1.byte_offset_from(p2); //~ERROR: different pointers without provenance + } +} diff --git a/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr b/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr new file mode 100644 index 0000000000..6e9e5633fe --- /dev/null +++ b/tests/fail/intrinsics/ptr_offset_from_different_ints.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) + --> $DIR/ptr_offset_from_different_ints.rs:LL:CC + | +LL | let _ = p1.byte_offset_from(p2); + | ^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/ptr_offset_from_different_ints.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + From fb34667e6c5cc278b34ccf59c44249fae944226f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 9 May 2024 22:45:14 -0400 Subject: [PATCH 195/208] Make builtin_deref just return a Ty --- src/borrow_tracker/stacked_borrows/mod.rs | 2 +- src/borrow_tracker/tree_borrows/mod.rs | 2 +- src/intrinsics/mod.rs | 2 +- src/intrinsics/simd.rs | 2 +- src/shims/unix/foreign_items.rs | 3 +-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/borrow_tracker/stacked_borrows/mod.rs b/src/borrow_tracker/stacked_borrows/mod.rs index a6dd1d829c..3da8744626 100644 --- a/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/borrow_tracker/stacked_borrows/mod.rs @@ -136,7 +136,7 @@ impl NewPermission { cx: &crate::MiriInterpCx<'_, 'tcx>, ) -> Self { // `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling). - let pointee = ty.builtin_deref(true).unwrap().ty; + let pointee = ty.builtin_deref(true).unwrap(); if pointee.is_unpin(*cx.tcx, cx.param_env()) { // A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only // a weak protector). diff --git a/src/borrow_tracker/tree_borrows/mod.rs b/src/borrow_tracker/tree_borrows/mod.rs index fc5eb942a2..b5bf16d3d3 100644 --- a/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/borrow_tracker/tree_borrows/mod.rs @@ -173,7 +173,7 @@ impl<'tcx> NewPermission { cx: &crate::MiriInterpCx<'_, 'tcx>, zero_size: bool, ) -> Option { - let pointee = ty.builtin_deref(true).unwrap().ty; + let pointee = ty.builtin_deref(true).unwrap(); pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| { // Regular `Unpin` box, give it `noalias` but only a weak protector // because it is valid to deallocate it within the function. diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index effd7f6d54..9e7fc7a21f 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -117,7 +117,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "write_bytes" | "volatile_set_memory" => { let [ptr, val_byte, count] = check_arg_count(args)?; - let ty = ptr.layout.ty.builtin_deref(true).unwrap().ty; + let ty = ptr.layout.ty.builtin_deref(true).unwrap(); let ty_layout = this.layout_of(ty)?; let val_byte = this.read_scalar(val_byte)?.to_u8()?; let ptr = this.read_pointer(ptr)?; diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index 3be98d7f5f..d0a78429ca 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -267,7 +267,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Op::WrappingOffset => { let ptr = left.to_scalar().to_pointer(this)?; let offset_count = right.to_scalar().to_target_isize(this)?; - let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty; + let pointee_ty = left.layout.ty.builtin_deref(true).unwrap(); let pointee_size = i64::try_from(this.layout_of(pointee_ty)?.size.bytes()).unwrap(); let offset_bytes = offset_count.wrapping_mul(pointee_size); diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 595cf64a4e..aaf47248af 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -378,8 +378,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .builtin_deref(true) .ok_or_else(|| err_ub_format!( "wrong signature used for `pthread_key_create`: first argument must be a raw pointer." - ))? - .ty; + ))?; let key_layout = this.layout_of(key_type)?; // Create key and write it into the memory where `key_ptr` wants it. From 73cbb49fbffda098dac39b7af1f4f175426daca5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 10 May 2024 18:01:36 +0200 Subject: [PATCH 196/208] rename 'extern-so' to 'native-lib' --- .gitignore | 2 +- README.md | 12 +- src/bin/miri.rs | 13 +- src/eval.rs | 6 +- src/machine.rs | 12 +- src/shims/ffi_support.rs | 289 ------------------ src/shims/foreign_items.rs | 6 +- src/shims/mod.rs | 4 +- src/shims/native_lib.rs | 242 +++++++++++++++ .../fail/function_not_in_so.rs | 0 .../fail/function_not_in_so.stderr | 0 tests/{extern-so => native-lib}/libtest.map | 0 .../pass/call_extern_c_fn.rs | 0 .../pass/call_extern_c_fn.stdout | 0 tests/{extern-so => native-lib}/test.c | 0 tests/ui.rs | 30 +- 16 files changed, 283 insertions(+), 333 deletions(-) delete mode 100644 src/shims/ffi_support.rs create mode 100644 src/shims/native_lib.rs rename tests/{extern-so => native-lib}/fail/function_not_in_so.rs (100%) rename tests/{extern-so => native-lib}/fail/function_not_in_so.stderr (100%) rename tests/{extern-so => native-lib}/libtest.map (100%) rename tests/{extern-so => native-lib}/pass/call_extern_c_fn.rs (100%) rename tests/{extern-so => native-lib}/pass/call_extern_c_fn.stdout (100%) rename tests/{extern-so => native-lib}/test.c (100%) diff --git a/.gitignore b/.gitignore index 924a93e807..97e006e8b1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,5 @@ tex/*/out perf.data perf.data.old flamegraph.svg -tests/extern-so/libtestlib.so +tests/native-lib/libtestlib.so .auto-* diff --git a/README.md b/README.md index 92cb7b3782..208a8b9ee6 100644 --- a/README.md +++ b/README.md @@ -374,17 +374,17 @@ to Miri failing to detect cases of undefined behavior in a program. this flag is **unsound**. * `-Zmiri-disable-weak-memory-emulation` disables the emulation of some C++11 weak memory effects. -* `-Zmiri-extern-so-file=` is an experimental flag for providing support - for FFI calls. Functions not provided by that file are still executed via the usual Miri shims. - **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause undefined behaviour in Miri itself! - And of course, Miri cannot do any checks on the actions taken by the external code. +* `-Zmiri-native-lib=` is an experimental flag for providing support + for calling native functions from inside the interpreter via FFI. Functions not provided by that + file are still executed via the usual Miri shims. + **WARNING**: If an invalid/incorrect `.so` file is specified, this can cause Undefined Behavior in Miri itself! + And of course, Miri cannot do any checks on the actions taken by the native code. Note that Miri has its own handling of file descriptors, so if you want to replace *some* functions working on file descriptors, you will have to replace *all* of them, or the two kinds of file descriptors will be mixed up. This is **work in progress**; currently, only integer arguments and return values are supported (and no, pointer/integer casts to work around this limitation will not work; - they will fail horribly). It also only works on unix hosts for now. - Follow [the discussion on supporting other types](https://github.com/rust-lang/miri/issues/2365). + they will fail horribly). It also only works on Linux hosts for now. * `-Zmiri-measureme=` enables `measureme` profiling for the interpreted program. This can be used to find which parts of your program are executing slowly under Miri. The profile is written out to a file inside a directory called ``, and can be processed diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 44201cb89a..305e9cd8d3 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -575,18 +575,15 @@ fn main() { "full" => BacktraceStyle::Full, _ => show_error!("-Zmiri-backtrace may only be 0, 1, or full"), }; - } else if let Some(param) = arg.strip_prefix("-Zmiri-extern-so-file=") { + } else if let Some(param) = arg.strip_prefix("-Zmiri-native-lib=") { let filename = param.to_string(); if std::path::Path::new(&filename).exists() { - if let Some(other_filename) = miri_config.external_so_file { - show_error!( - "-Zmiri-extern-so-file is already set to {}", - other_filename.display() - ); + if let Some(other_filename) = miri_config.native_lib { + show_error!("-Zmiri-native-lib is already set to {}", other_filename.display()); } - miri_config.external_so_file = Some(filename.into()); + miri_config.native_lib = Some(filename.into()); } else { - show_error!("-Zmiri-extern-so-file `{}` does not exist", filename); + show_error!("-Zmiri-native-lib `{}` does not exist", filename); } } else if let Some(param) = arg.strip_prefix("-Zmiri-num-cpus=") { let num_cpus = param diff --git a/src/eval.rs b/src/eval.rs index 97c5e9a0ea..78c092faf9 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -142,8 +142,8 @@ pub struct MiriConfig { /// Whether Stacked Borrows and Tree Borrows retagging should recurse into fields of datatypes. pub retag_fields: RetagFields, /// The location of a shared object file to load when calling external functions - /// FIXME! consider allowing users to specify paths to multiple SO files, or to a directory - pub external_so_file: Option, + /// FIXME! consider allowing users to specify paths to multiple files, or to a directory + pub native_lib: Option, /// Run a garbage collector for BorTags every N basic blocks. pub gc_interval: u32, /// The number of CPUs to be reported by miri. @@ -188,7 +188,7 @@ impl Default for MiriConfig { preemption_rate: 0.01, // 1% report_progress: None, retag_fields: RetagFields::Yes, - external_so_file: None, + native_lib: None, gc_interval: 10_000, num_cpus: 1, page_size: None, diff --git a/src/machine.rs b/src/machine.rs index 51b96bff5f..8854b18528 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -535,11 +535,11 @@ pub struct MiriMachine<'mir, 'tcx> { // The total number of blocks that have been executed. pub(crate) basic_block_count: u64, - /// Handle of the optional shared object file for external functions. + /// Handle of the optional shared object file for native functions. #[cfg(target_os = "linux")] - pub external_so_lib: Option<(libloading::Library, std::path::PathBuf)>, + pub native_lib: Option<(libloading::Library, std::path::PathBuf)>, #[cfg(not(target_os = "linux"))] - pub external_so_lib: Option, + pub native_lib: Option, /// Run a garbage collector for BorTags every N basic blocks. pub(crate) gc_interval: u32, @@ -665,7 +665,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { basic_block_count: 0, clock: Clock::new(config.isolated_op == IsolatedOp::Allow), #[cfg(target_os = "linux")] - external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| { + native_lib: config.native_lib.as_ref().map(|lib_file_path| { let target_triple = layout_cx.tcx.sess.opts.target_triple.triple(); // Check if host target == the session target. if env!("TARGET") != target_triple { @@ -687,7 +687,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { ) }), #[cfg(not(target_os = "linux"))] - external_so_lib: config.external_so_file.as_ref().map(|_| { + native_lib: config.native_lib.as_ref().map(|_| { panic!("loading external .so files is only supported on Linux") }), gc_interval: config.gc_interval, @@ -802,7 +802,7 @@ impl VisitProvenance for MiriMachine<'_, '_> { preemption_rate: _, report_progress: _, basic_block_count: _, - external_so_lib: _, + native_lib: _, gc_interval: _, since_gc: _, num_cpus: _, diff --git a/src/shims/ffi_support.rs b/src/shims/ffi_support.rs deleted file mode 100644 index 6da119e5fa..0000000000 --- a/src/shims/ffi_support.rs +++ /dev/null @@ -1,289 +0,0 @@ -use libffi::{high::call as ffi, low::CodePtr}; -use std::ops::Deref; - -use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy}; -use rustc_span::Symbol; -use rustc_target::abi::HasDataLayout; - -use crate::*; - -impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} - -pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Extract the scalar value from the result of reading a scalar from the machine, - /// and convert it to a `CArg`. - fn scalar_to_carg( - k: Scalar, - arg_type: Ty<'tcx>, - cx: &impl HasDataLayout, - ) -> InterpResult<'tcx, CArg> { - match arg_type.kind() { - // If the primitive provided can be converted to a type matching the type pattern - // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. - // the ints - ty::Int(IntTy::I8) => { - return Ok(CArg::Int8(k.to_i8()?)); - } - ty::Int(IntTy::I16) => { - return Ok(CArg::Int16(k.to_i16()?)); - } - ty::Int(IntTy::I32) => { - return Ok(CArg::Int32(k.to_i32()?)); - } - ty::Int(IntTy::I64) => { - return Ok(CArg::Int64(k.to_i64()?)); - } - ty::Int(IntTy::Isize) => { - // This will fail if host != target, but then the entire FFI thing probably won't work well - // in that situation. - return Ok(CArg::ISize(k.to_target_isize(cx)?.try_into().unwrap())); - } - // the uints - ty::Uint(UintTy::U8) => { - return Ok(CArg::UInt8(k.to_u8()?)); - } - ty::Uint(UintTy::U16) => { - return Ok(CArg::UInt16(k.to_u16()?)); - } - ty::Uint(UintTy::U32) => { - return Ok(CArg::UInt32(k.to_u32()?)); - } - ty::Uint(UintTy::U64) => { - return Ok(CArg::UInt64(k.to_u64()?)); - } - ty::Uint(UintTy::Usize) => { - // This will fail if host != target, but then the entire FFI thing probably won't work well - // in that situation. - return Ok(CArg::USize(k.to_target_usize(cx)?.try_into().unwrap())); - } - _ => {} - } - // If no primitives were returned then we have an unsupported type. - throw_unsup_format!( - "unsupported scalar argument type to external C function: {:?}", - arg_type - ); - } - - /// Call external C function and - /// store output, depending on return type in the function signature. - fn call_external_c_and_store_return<'a>( - &mut self, - link_name: Symbol, - dest: &MPlaceTy<'tcx, Provenance>, - ptr: CodePtr, - libffi_args: Vec>, - ) -> InterpResult<'tcx, ()> { - let this = self.eval_context_mut(); - - // Unsafe because of the call to external C code. - // Because this is calling a C function it is not necessarily sound, - // but there is no way around this and we've checked as much as we can. - unsafe { - // If the return type of a function is a primitive integer type, - // then call the function (`ptr`) with arguments `libffi_args`, store the return value as the specified - // primitive integer type, and then write this value out to the miri memory as an integer. - match dest.layout.ty.kind() { - // ints - ty::Int(IntTy::I8) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::I16) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::I32) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::I64) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Int(IntTy::Isize) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - // `isize` doesn't `impl Into`, so convert manually. - // Convert to `i64` since this covers both 32- and 64-bit machines. - this.write_int(i64::try_from(x).unwrap(), dest)?; - return Ok(()); - } - // uints - ty::Uint(UintTy::U8) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::U16) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::U32) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::U64) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - this.write_int(x, dest)?; - return Ok(()); - } - ty::Uint(UintTy::Usize) => { - let x = ffi::call::(ptr, libffi_args.as_slice()); - // `usize` doesn't `impl Into`, so convert manually. - // Convert to `u64` since this covers both 32- and 64-bit machines. - this.write_int(u64::try_from(x).unwrap(), dest)?; - return Ok(()); - } - // Functions with no declared return type (i.e., the default return) - // have the output_type `Tuple([])`. - ty::Tuple(t_list) => - if t_list.len() == 0 { - ffi::call::<()>(ptr, libffi_args.as_slice()); - return Ok(()); - }, - _ => {} - } - // FIXME ellen! deal with all the other return types - throw_unsup_format!("unsupported return type to external C function: {:?}", link_name); - } - } - - /// Get the pointer to the function of the specified name in the shared object file, - /// if it exists. The function must be in the shared object file specified: we do *not* - /// return pointers to functions in dependencies of the library. - fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { - let this = self.eval_context_mut(); - // Try getting the function from the shared library. - // On windows `_lib_path` will be unused, hence the name starting with `_`. - let (lib, _lib_path) = this.machine.external_so_lib.as_ref().unwrap(); - let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { - match lib.get(link_name.as_str().as_bytes()) { - Ok(x) => x, - Err(_) => { - return None; - } - } - }; - - // FIXME: this is a hack! - // The `libloading` crate will automatically load system libraries like `libc`. - // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 - // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the - // library if it can't find the symbol in the library itself. - // So, in order to check if the function was actually found in the specified - // `machine.external_so_lib` we need to check its `dli_fname` and compare it to - // the specified SO file path. - // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, - // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 - // using the `libc` crate where this interface is public. - // No `libc::dladdr` on windows. - let mut info = std::mem::MaybeUninit::::uninit(); - unsafe { - if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { - if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() - != _lib_path.to_str().unwrap() - { - return None; - } - } - } - // Return a pointer to the function. - Some(CodePtr(*func.deref() as *mut _)) - } - - /// Call specified external C function, with supplied arguments. - /// Need to convert all the arguments from their hir representations to - /// a form compatible with C (through `libffi` call). - /// Then, convert return from the C call into a corresponding form that - /// can be stored in Miri internal memory. - fn call_external_c_fct( - &mut self, - link_name: Symbol, - dest: &MPlaceTy<'tcx, Provenance>, - args: &[OpTy<'tcx, Provenance>], - ) -> InterpResult<'tcx, bool> { - // Get the pointer to the function in the shared object file if it exists. - let code_ptr = match self.get_func_ptr_explicitly_from_lib(link_name) { - Some(ptr) => ptr, - None => { - // Shared object file does not export this function -- try the shims next. - return Ok(false); - } - }; - - let this = self.eval_context_mut(); - - // Get the function arguments, and convert them to `libffi`-compatible form. - let mut libffi_args = Vec::::with_capacity(args.len()); - for cur_arg in args.iter() { - libffi_args.push(Self::scalar_to_carg( - this.read_scalar(cur_arg)?, - cur_arg.layout.ty, - this, - )?); - } - - // Convert them to `libffi::high::Arg` type. - let libffi_args = libffi_args - .iter() - .map(|cur_arg| cur_arg.arg_downcast()) - .collect::>>(); - - // Call the function and store output, depending on return type in the function signature. - self.call_external_c_and_store_return(link_name, dest, code_ptr, libffi_args)?; - Ok(true) - } -} - -#[derive(Debug, Clone)] -/// Enum of supported arguments to external C functions. -// We introduce this enum instead of just calling `ffi::arg` and storing a list -// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference -// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html -// and we need to store a copy of the value, and pass a reference to this copy to C instead. -pub enum CArg { - /// 8-bit signed integer. - Int8(i8), - /// 16-bit signed integer. - Int16(i16), - /// 32-bit signed integer. - Int32(i32), - /// 64-bit signed integer. - Int64(i64), - /// isize. - ISize(isize), - /// 8-bit unsigned integer. - UInt8(u8), - /// 16-bit unsigned integer. - UInt16(u16), - /// 32-bit unsigned integer. - UInt32(u32), - /// 64-bit unsigned integer. - UInt64(u64), - /// usize. - USize(usize), -} - -impl<'a> CArg { - /// Convert a `CArg` to a `libffi` argument type. - fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { - match self { - CArg::Int8(i) => ffi::arg(i), - CArg::Int16(i) => ffi::arg(i), - CArg::Int32(i) => ffi::arg(i), - CArg::Int64(i) => ffi::arg(i), - CArg::ISize(i) => ffi::arg(i), - CArg::UInt8(i) => ffi::arg(i), - CArg::UInt16(i) => ffi::arg(i), - CArg::UInt32(i) => ffi::arg(i), - CArg::UInt64(i) => ffi::arg(i), - CArg::USize(i) => ffi::arg(i), - } - } -} diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 28028479ac..1185d440ec 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -211,12 +211,12 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // First deal with any external C functions in linked .so file. #[cfg(target_os = "linux")] - if this.machine.external_so_lib.as_ref().is_some() { - use crate::shims::ffi_support::EvalContextExt as _; + if this.machine.native_lib.as_ref().is_some() { + use crate::shims::native_lib::EvalContextExt as _; // An Ok(false) here means that the function being called was not exported // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. - if this.call_external_c_fct(link_name, dest, args)? { + if this.call_native_fn(link_name, dest, args)? { return Ok(EmulateItemResult::NeedsJumping); } } diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 1104845920..aaa3c69b92 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -2,9 +2,9 @@ mod alloc; mod backtrace; -#[cfg(target_os = "linux")] -pub mod ffi_support; pub mod foreign_items; +#[cfg(target_os = "linux")] +pub mod native_lib; pub mod unix; pub mod windows; mod x86; diff --git a/src/shims/native_lib.rs b/src/shims/native_lib.rs new file mode 100644 index 0000000000..f9b8563b4b --- /dev/null +++ b/src/shims/native_lib.rs @@ -0,0 +1,242 @@ +//! Implements calling functions from a native library. +use libffi::{high::call as ffi, low::CodePtr}; +use std::ops::Deref; + +use rustc_middle::ty::{self as ty, IntTy, UintTy}; +use rustc_span::Symbol; +use rustc_target::abi::{Abi, HasDataLayout}; + +use crate::*; + +impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Call native host function and return the output as an immediate. + fn call_native_with_args<'a>( + &mut self, + link_name: Symbol, + dest: &MPlaceTy<'tcx, Provenance>, + ptr: CodePtr, + libffi_args: Vec>, + ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> { + let this = self.eval_context_mut(); + + // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value + // as the specified primitive integer type + let scalar = match dest.layout.ty.kind() { + // ints + ty::Int(IntTy::I8) => { + // Unsafe because of the call to native code. + // Because this is calling a C function it is not necessarily sound, + // but there is no way around this and we've checked as much as we can. + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i8(x) + } + ty::Int(IntTy::I16) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i16(x) + } + ty::Int(IntTy::I32) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i32(x) + } + ty::Int(IntTy::I64) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_i64(x) + } + ty::Int(IntTy::Isize) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_target_isize(x.try_into().unwrap(), this) + } + // uints + ty::Uint(UintTy::U8) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u8(x) + } + ty::Uint(UintTy::U16) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u16(x) + } + ty::Uint(UintTy::U32) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u32(x) + } + ty::Uint(UintTy::U64) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_u64(x) + } + ty::Uint(UintTy::Usize) => { + let x = unsafe { ffi::call::(ptr, libffi_args.as_slice()) }; + Scalar::from_target_usize(x.try_into().unwrap(), this) + } + // Functions with no declared return type (i.e., the default return) + // have the output_type `Tuple([])`. + ty::Tuple(t_list) if t_list.len() == 0 => { + unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) }; + return Ok(ImmTy::uninit(dest.layout)); + } + _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name), + }; + Ok(ImmTy::from_scalar(scalar, dest.layout)) + } + + /// Get the pointer to the function of the specified name in the shared object file, + /// if it exists. The function must be in the shared object file specified: we do *not* + /// return pointers to functions in dependencies of the library. + fn get_func_ptr_explicitly_from_lib(&mut self, link_name: Symbol) -> Option { + let this = self.eval_context_mut(); + // Try getting the function from the shared library. + // On windows `_lib_path` will be unused, hence the name starting with `_`. + let (lib, _lib_path) = this.machine.native_lib.as_ref().unwrap(); + let func: libloading::Symbol<'_, unsafe extern "C" fn()> = unsafe { + match lib.get(link_name.as_str().as_bytes()) { + Ok(x) => x, + Err(_) => { + return None; + } + } + }; + + // FIXME: this is a hack! + // The `libloading` crate will automatically load system libraries like `libc`. + // On linux `libloading` is based on `dlsym`: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#202 + // and `dlsym`(https://linux.die.net/man/3/dlsym) looks through the dependency tree of the + // library if it can't find the symbol in the library itself. + // So, in order to check if the function was actually found in the specified + // `machine.external_so_lib` we need to check its `dli_fname` and compare it to + // the specified SO file path. + // This code is a reimplementation of the mechanism for getting `dli_fname` in `libloading`, + // from: https://docs.rs/libloading/0.7.3/src/libloading/os/unix/mod.rs.html#411 + // using the `libc` crate where this interface is public. + let mut info = std::mem::MaybeUninit::::uninit(); + unsafe { + if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 { + if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap() + != _lib_path.to_str().unwrap() + { + return None; + } + } + } + // Return a pointer to the function. + Some(CodePtr(*func.deref() as *mut _)) + } +} + +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { + /// Call the native host function, with supplied arguments. + /// Needs to convert all the arguments from their Miri representations to + /// a native form (through `libffi` call). + /// Then, convert the return value from the native form into something that + /// can be stored in Miri's internal memory. + fn call_native_fn( + &mut self, + link_name: Symbol, + dest: &MPlaceTy<'tcx, Provenance>, + args: &[OpTy<'tcx, Provenance>], + ) -> InterpResult<'tcx, bool> { + let this = self.eval_context_mut(); + // Get the pointer to the function in the shared object file if it exists. + let code_ptr = match this.get_func_ptr_explicitly_from_lib(link_name) { + Some(ptr) => ptr, + None => { + // Shared object file does not export this function -- try the shims next. + return Ok(false); + } + }; + + // Get the function arguments, and convert them to `libffi`-compatible form. + let mut libffi_args = Vec::::with_capacity(args.len()); + for arg in args.iter() { + if !matches!(arg.layout.abi, Abi::Scalar(_)) { + throw_unsup_format!("only scalar argument types are support for native calls") + } + libffi_args.push(imm_to_carg(this.read_immediate(arg)?, this)?); + } + + // Convert them to `libffi::high::Arg` type. + let libffi_args = libffi_args + .iter() + .map(|arg| arg.arg_downcast()) + .collect::>>(); + + // Call the function and store output, depending on return type in the function signature. + let ret = this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?; + this.write_immediate(*ret, dest)?; + Ok(true) + } +} + +#[derive(Debug, Clone)] +/// Enum of supported arguments to external C functions. +// We introduce this enum instead of just calling `ffi::arg` and storing a list +// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference +// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html +// and we need to store a copy of the value, and pass a reference to this copy to C instead. +enum CArg { + /// 8-bit signed integer. + Int8(i8), + /// 16-bit signed integer. + Int16(i16), + /// 32-bit signed integer. + Int32(i32), + /// 64-bit signed integer. + Int64(i64), + /// isize. + ISize(isize), + /// 8-bit unsigned integer. + UInt8(u8), + /// 16-bit unsigned integer. + UInt16(u16), + /// 32-bit unsigned integer. + UInt32(u32), + /// 64-bit unsigned integer. + UInt64(u64), + /// usize. + USize(usize), +} + +impl<'a> CArg { + /// Convert a `CArg` to a `libffi` argument type. + fn arg_downcast(&'a self) -> libffi::high::Arg<'a> { + match self { + CArg::Int8(i) => ffi::arg(i), + CArg::Int16(i) => ffi::arg(i), + CArg::Int32(i) => ffi::arg(i), + CArg::Int64(i) => ffi::arg(i), + CArg::ISize(i) => ffi::arg(i), + CArg::UInt8(i) => ffi::arg(i), + CArg::UInt16(i) => ffi::arg(i), + CArg::UInt32(i) => ffi::arg(i), + CArg::UInt64(i) => ffi::arg(i), + CArg::USize(i) => ffi::arg(i), + } + } +} + +/// Extract the scalar value from the result of reading a scalar from the machine, +/// and convert it to a `CArg`. +fn imm_to_carg<'tcx>( + v: ImmTy<'tcx, Provenance>, + cx: &impl HasDataLayout, +) -> InterpResult<'tcx, CArg> { + Ok(match v.layout.ty.kind() { + // If the primitive provided can be converted to a type matching the type pattern + // then create a `CArg` of this primitive value with the corresponding `CArg` constructor. + // the ints + ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?), + ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?), + ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?), + ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?), + ty::Int(IntTy::Isize) => + CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()), + // the uints + ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?), + ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?), + ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?), + ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?), + ty::Uint(UintTy::Usize) => + CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()), + _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty), + }) +} diff --git a/tests/extern-so/fail/function_not_in_so.rs b/tests/native-lib/fail/function_not_in_so.rs similarity index 100% rename from tests/extern-so/fail/function_not_in_so.rs rename to tests/native-lib/fail/function_not_in_so.rs diff --git a/tests/extern-so/fail/function_not_in_so.stderr b/tests/native-lib/fail/function_not_in_so.stderr similarity index 100% rename from tests/extern-so/fail/function_not_in_so.stderr rename to tests/native-lib/fail/function_not_in_so.stderr diff --git a/tests/extern-so/libtest.map b/tests/native-lib/libtest.map similarity index 100% rename from tests/extern-so/libtest.map rename to tests/native-lib/libtest.map diff --git a/tests/extern-so/pass/call_extern_c_fn.rs b/tests/native-lib/pass/call_extern_c_fn.rs similarity index 100% rename from tests/extern-so/pass/call_extern_c_fn.rs rename to tests/native-lib/pass/call_extern_c_fn.rs diff --git a/tests/extern-so/pass/call_extern_c_fn.stdout b/tests/native-lib/pass/call_extern_c_fn.stdout similarity index 100% rename from tests/extern-so/pass/call_extern_c_fn.stdout rename to tests/native-lib/pass/call_extern_c_fn.stdout diff --git a/tests/extern-so/test.c b/tests/native-lib/test.c similarity index 100% rename from tests/extern-so/test.c rename to tests/native-lib/test.c diff --git a/tests/ui.rs b/tests/ui.rs index efeefbe29f..1a7c472450 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -27,11 +27,12 @@ pub fn flagsplit(flags: &str) -> Vec { flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect() } -// Build the shared object file for testing external C function calls. -fn build_so_for_c_ffi_tests() -> PathBuf { +// Build the shared object file for testing native function calls. +fn build_native_lib() -> PathBuf { let cc = option_env!("CC").unwrap_or("cc"); // Target directory that we can write to. - let so_target_dir = Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-extern-so"); + let so_target_dir = + Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-native-lib"); // Create the directory if it does not already exist. std::fs::create_dir_all(&so_target_dir) .expect("Failed to create directory for shared object file"); @@ -41,18 +42,18 @@ fn build_so_for_c_ffi_tests() -> PathBuf { "-shared", "-o", so_file_path.to_str().unwrap(), - "tests/extern-so/test.c", + "tests/native-lib/test.c", // Only add the functions specified in libcode.version to the shared object file. // This is to avoid automatically adding `malloc`, etc. // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/ "-fPIC", - "-Wl,--version-script=tests/extern-so/libtest.map", + "-Wl,--version-script=tests/native-lib/libtest.map", ]) .output() - .expect("failed to generate shared object file for testing external C function calls"); + .expect("failed to generate shared object file for testing native function calls"); if !cc_output.status.success() { panic!( - "error in generating shared object file for testing external C function calls:\n{}", + "error generating shared object file for testing native function calls:\n{}", String::from_utf8_lossy(&cc_output.stderr), ); } @@ -132,13 +133,12 @@ fn run_tests( config.program.args.push("--target".into()); config.program.args.push(target.into()); - // If we're testing the extern-so functionality, then build the shared object file for testing + // If we're testing the native-lib functionality, then build the shared object file for testing // external C function calls and push the relevant compiler flag. - if path.starts_with("tests/extern-so/") { - assert!(cfg!(target_os = "linux")); - let so_file_path = build_so_for_c_ffi_tests(); - let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file="); - flag.push(so_file_path.into_os_string()); + if path.starts_with("tests/native-lib/") { + let native_lib = build_native_lib(); + let mut flag = std::ffi::OsString::from("-Zmiri-native-lib="); + flag.push(native_lib.into_os_string()); config.program.args.push(flag); } @@ -292,10 +292,10 @@ fn main() -> Result<()> { tmpdir.path(), )?; if cfg!(target_os = "linux") { - ui(Mode::Pass, "tests/extern-so/pass", &target, WithoutDependencies, tmpdir.path())?; + ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?; ui( Mode::Fail { require_patterns: true, rustfix: RustfixMode::Disabled }, - "tests/extern-so/fail", + "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path(), From bd250395aeaaa79f85a08f5e941561ddd6bb79bb Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 11 May 2024 04:56:14 +0000 Subject: [PATCH 197/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index ca6f4d5091..3636c856d0 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -d568423a7a4ddb4b49323d96078a22f94df55fbd +ef15976387ad9c1cdceaabf469e0cf35f5852f6d From 5ca302683951e29a5ea9e08cbc790ce3960fa069 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 11 May 2024 10:27:20 +0200 Subject: [PATCH 198/208] alloc: update comments around malloc() alignment --- src/shims/alloc.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/shims/alloc.rs b/src/shims/alloc.rs index 4eefb8b439..3cf443eea3 100644 --- a/src/shims/alloc.rs +++ b/src/shims/alloc.rs @@ -22,20 +22,33 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Returns the minimum alignment for the target architecture for allocations of the given size. fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { let this = self.eval_context_ref(); - // List taken from `library/std/src/sys/pal/common/alloc.rs`. - // This list should be kept in sync with the one from libstd. - let min_align = match this.tcx.sess.target.arch.as_ref() { + // The C standard says: "The pointer returned if the allocation succeeds is suitably aligned + // so that it may be assigned to a pointer to any type of object with a fundamental + // alignment requirement and size less than or equal to the size requested." + // So first we need to figure out what the limits are for "fundamental alignment". + // This is given by `alignof(max_align_t)`. The following list is taken from + // `library/std/src/sys/pal/common/alloc.rs` (where this is called `MIN_ALIGN`) and should + // be kept in sync. + let max_fundamental_align = match this.tcx.sess.target.arch.as_ref() { "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8, "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" => 16, arch => bug!("unsupported target architecture for malloc: `{}`", arch), }; - // Windows always aligns, even small allocations. - // Source: - // But jemalloc does not, so for the C heap we only align if the allocation is sufficiently big. - if kind == MiriMemoryKind::WinHeap || size >= min_align { - return Align::from_bytes(min_align).unwrap(); + // The C standard only requires sufficient alignment for any *type* with size less than or + // equal to the size requested. Types one can define in standard C seem to never have an alignment + // bigger than their size. So if the size is 2, then only alignment 2 is guaranteed, even if + // `max_fundamental_align` is bigger. + // This matches what some real-world implementations do, see e.g. + // - https://github.com/jemalloc/jemalloc/issues/1533 + // - https://github.com/llvm/llvm-project/issues/53540 + // - https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm + // However, Windows `HeapAlloc` always aligns, even small allocations, so it gets different treatment here. + // Source: + if kind == MiriMemoryKind::WinHeap || size >= max_fundamental_align { + return Align::from_bytes(max_fundamental_align).unwrap(); } + // C doesn't have zero-sized types, so presumably nothing is guaranteed here. if size == 0 { return Align::ONE; } From fd94aeac4a63999db68c007a1d14f7deb771d355 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 11 May 2024 10:48:36 +0200 Subject: [PATCH 199/208] separate windows heap functions from C heap shims --- src/lib.rs | 1 + src/shims/alloc.rs | 28 ++++++----------- src/shims/foreign_items.rs | 8 ++--- src/shims/unix/foreign_items.rs | 2 +- src/shims/windows/foreign_items.rs | 50 ++++++++++++++++++++++++------ 5 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1680b98eca..54eb6a3bd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ #![feature(let_chains)] #![feature(lint_reasons)] #![feature(trait_upcasting)] +#![feature(strict_overflow_ops)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/shims/alloc.rs b/src/shims/alloc.rs index 3cf443eea3..1deb9a5654 100644 --- a/src/shims/alloc.rs +++ b/src/shims/alloc.rs @@ -19,8 +19,8 @@ pub(super) fn check_alloc_request<'tcx>(size: u64, align: u64) -> InterpResult<' impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {} pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { - /// Returns the minimum alignment for the target architecture for allocations of the given size. - fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align { + /// Returns the alignment that `malloc` would guarantee for requests of the given size. + fn malloc_align(&self, size: u64) -> Align { let this = self.eval_context_ref(); // The C standard says: "The pointer returned if the allocation succeeds is suitably aligned // so that it may be assigned to a pointer to any type of object with a fundamental @@ -43,9 +43,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // - https://github.com/jemalloc/jemalloc/issues/1533 // - https://github.com/llvm/llvm-project/issues/53540 // - https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm - // However, Windows `HeapAlloc` always aligns, even small allocations, so it gets different treatment here. - // Source: - if kind == MiriMemoryKind::WinHeap || size >= max_fundamental_align { + if size >= max_fundamental_align { return Align::from_bytes(max_fundamental_align).unwrap(); } // C doesn't have zero-sized types, so presumably nothing is guaranteed here. @@ -98,11 +96,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, size: u64, zero_init: bool, - kind: MiriMemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); - let align = this.min_align(size, kind); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, kind.into())?; + let align = this.malloc_align(size); + let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?; if zero_init { // We just allocated this, the access is definitely in-bounds and fits into our address space. this.write_bytes_ptr( @@ -114,14 +111,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(ptr.into()) } - fn free( - &mut self, - ptr: Pointer>, - kind: MiriMemoryKind, - ) -> InterpResult<'tcx> { + fn free(&mut self, ptr: Pointer>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); if !this.ptr_is_null(ptr)? { - this.deallocate_ptr(ptr, None, kind.into())?; + this.deallocate_ptr(ptr, None, MiriMemoryKind::C.into())?; } Ok(()) } @@ -130,13 +123,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { &mut self, old_ptr: Pointer>, new_size: u64, - kind: MiriMemoryKind, ) -> InterpResult<'tcx, Pointer>> { let this = self.eval_context_mut(); - let new_align = this.min_align(new_size, kind); + let new_align = this.malloc_align(new_size); if this.ptr_is_null(old_ptr)? { // Here we must behave like `malloc`. - self.malloc(new_size, /*zero_init*/ false, kind) + self.malloc(new_size, /*zero_init*/ false) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. @@ -148,7 +140,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { None, Size::from_bytes(new_size), new_align, - kind.into(), + MiriMemoryKind::C.into(), )?; Ok(new_ptr.into()) } diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 1185d440ec..d431c28d55 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -421,7 +421,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "malloc" => { let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let size = this.read_target_usize(size)?; - let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?; + let res = this.malloc(size, /*zero_init:*/ false)?; this.write_pointer(res, dest)?; } "calloc" => { @@ -432,20 +432,20 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = items .checked_mul(len) .ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?; - let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?; + let res = this.malloc(size, /*zero_init:*/ true)?; this.write_pointer(res, dest)?; } "free" => { let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; - this.free(ptr, MiriMemoryKind::C)?; + this.free(ptr)?; } "realloc" => { let [old_ptr, new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; let old_ptr = this.read_pointer(old_ptr)?; let new_size = this.read_target_usize(new_size)?; - let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?; + let res = this.realloc(old_ptr, new_size)?; this.write_pointer(res, dest)?; } diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 595cf64a4e..cec7f1bfd3 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -310,7 +310,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_null(dest)?; } Some(len) => { - let res = this.realloc(ptr, len, MiriMemoryKind::C)?; + let res = this.realloc(ptr, len)?; this.write_pointer(res, dest)?; } } diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 44b0f25b55..086abf19c5 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -5,10 +5,9 @@ use std::path::{self, Path, PathBuf}; use std::str; use rustc_span::Symbol; -use rustc_target::abi::Size; +use rustc_target::abi::{Align, Size}; use rustc_target::spec::abi::Abi; -use crate::shims::alloc::EvalContextExt as _; use crate::shims::os_str::bytes_to_os_str; use crate::shims::windows::*; use crate::*; @@ -248,8 +247,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = this.read_target_usize(size)?; let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY let zero_init = (flags & heap_zero_memory) == heap_zero_memory; - let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?; - this.write_pointer(res, dest)?; + // Alignment is twice the pointer size. + // Source: + let align = this.tcx.pointer_size().bytes().strict_mul(2); + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::WinHeap.into(), + )?; + if zero_init { + this.write_bytes_ptr( + ptr.into(), + iter::repeat(0u8).take(usize::try_from(size).unwrap()), + )?; + } + this.write_pointer(ptr, dest)?; } "HeapFree" => { let [handle, flags, ptr] = @@ -257,23 +269,41 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; - this.free(ptr, MiriMemoryKind::WinHeap)?; + // "This pointer can be NULL." It doesn't say what happens then, but presumably nothing. + // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree) + if !this.ptr_is_null(ptr)? { + this.deallocate_ptr(ptr, None, MiriMemoryKind::WinHeap.into())?; + } this.write_scalar(Scalar::from_i32(1), dest)?; } "HeapReAlloc" => { - let [handle, flags, ptr, size] = + let [handle, flags, old_ptr, size] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; - let ptr = this.read_pointer(ptr)?; + let old_ptr = this.read_pointer(old_ptr)?; let size = this.read_target_usize(size)?; - let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?; - this.write_pointer(res, dest)?; + let align = this.tcx.pointer_size().bytes().strict_mul(2); // same as above + // The docs say that `old_ptr` must come from an earlier HeapAlloc or HeapReAlloc, + // so unlike C `realloc` we do *not* allow a NULL here. + // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc) + let new_ptr = this.reallocate_ptr( + old_ptr, + None, + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::WinHeap.into(), + )?; + this.write_pointer(new_ptr, dest)?; } "LocalFree" => { let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?; let ptr = this.read_pointer(ptr)?; - this.free(ptr, MiriMemoryKind::WinLocal)?; + // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL." + // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree) + if !this.ptr_is_null(ptr)? { + this.deallocate_ptr(ptr, None, MiriMemoryKind::WinLocal.into())?; + } this.write_null(dest)?; } From 18379e8e70c6ab5d79bb19d0b37c2a1d964928d6 Mon Sep 17 00:00:00 2001 From: bendn Date: Sat, 11 May 2024 08:41:04 +0700 Subject: [PATCH 200/208] support `f*_algebraic` --- src/intrinsics/mod.rs | 22 +++++++++++++ tests/pass/intrinsics/float_algebraic_math.rs | 32 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 tests/pass/intrinsics/float_algebraic_math.rs diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index effd7f6d54..e38a9e4d5e 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -256,6 +256,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + #[rustfmt::skip] + | "fadd_algebraic" + | "fsub_algebraic" + | "fmul_algebraic" + | "fdiv_algebraic" + | "frem_algebraic" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_algebraic" => mir::BinOp::Add, + "fsub_algebraic" => mir::BinOp::Sub, + "fmul_algebraic" => mir::BinOp::Mul, + "fdiv_algebraic" => mir::BinOp::Div, + "frem_algebraic" => mir::BinOp::Rem, + _ => bug!(), + }; + let res = this.wrapping_binary_op(op, &a, &b)?; + // `wrapping_binary_op` already called `generate_nan` if necessary. + this.write_immediate(*res, dest)?; + } #[rustfmt::skip] | "fadd_fast" diff --git a/tests/pass/intrinsics/float_algebraic_math.rs b/tests/pass/intrinsics/float_algebraic_math.rs new file mode 100644 index 0000000000..f6f083f7b5 --- /dev/null +++ b/tests/pass/intrinsics/float_algebraic_math.rs @@ -0,0 +1,32 @@ +#![feature(core_intrinsics)] + +use std::intrinsics::{ + fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic, +}; + +#[inline(never)] +pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); +} + +#[inline(never)] +pub fn test_operations_f32(a: f32, b: f32) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); +} + +fn main() { + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f32(11., 2.); + test_operations_f32(10., 15.); +} From e1222e341c14cf0e5cf117fd651a91cc10f03ff5 Mon Sep 17 00:00:00 2001 From: tiif Date: Sun, 12 May 2024 15:01:18 +0800 Subject: [PATCH 201/208] Add non-null pointer for posix_memalign --- src/shims/unix/foreign_items.rs | 16 +++++------- .../posix_memalign_size_zero_double_free.rs | 14 +++++++++++ ...osix_memalign_size_zero_double_free.stderr | 25 +++++++++++++++++++ .../libc/posix_memalign_size_zero_leak.rs | 10 ++++++++ .../libc/posix_memalign_size_zero_leak.stderr | 15 +++++++++++ tests/pass-dep/libc/libc-mem.rs | 7 +++--- 6 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs create mode 100644 tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr create mode 100644 tests/fail-dep/libc/posix_memalign_size_zero_leak.rs create mode 100644 tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index 08f64e499b..b5165548d1 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -259,16 +259,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let einval = this.eval_libc_i32("EINVAL"); this.write_int(einval, dest)?; } else { - if size == 0 { - this.write_null(&ret)?; - } else { - let ptr = this.allocate_ptr( - Size::from_bytes(size), - Align::from_bytes(align).unwrap(), - MiriMemoryKind::C.into(), - )?; - this.write_pointer(ptr, &ret)?; - } + let ptr = this.allocate_ptr( + Size::from_bytes(size), + Align::from_bytes(align).unwrap(), + MiriMemoryKind::C.into(), + )?; + this.write_pointer(ptr, &ret)?; this.write_null(dest)?; } } diff --git a/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs b/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs new file mode 100644 index 0000000000..b6b7b007f2 --- /dev/null +++ b/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs @@ -0,0 +1,14 @@ +//@ignore-target-windows: No posix_memalign on Windows + +use std::ptr; + +fn main() { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + unsafe { + let _ = libc::posix_memalign(&mut ptr, align, size); + libc::free(ptr); + libc::free(ptr); //~ERROR: dangling + } +} diff --git a/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr b/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr new file mode 100644 index 0000000000..3ed117c5a0 --- /dev/null +++ b/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling + --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information +help: ALLOC was allocated here: + --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC + | +LL | let _ = libc::posix_memalign(&mut ptr, align, size); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: ALLOC was deallocated here: + --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC + | +LL | libc::free(ptr); + | ^^^^^^^^^^^^^^^ + = note: BACKTRACE (of the first span): + = note: inside `main` at $DIR/posix_memalign_size_zero_double_free.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs b/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs new file mode 100644 index 0000000000..1a4c9605fe --- /dev/null +++ b/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No posix_memalign on Windows + +use std::ptr; + +fn main() { + let mut ptr: *mut libc::c_void = ptr::null_mut(); + let align = 64; + let size = 0; + let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) }; //~ERROR: memory leak +} diff --git a/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr b/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr new file mode 100644 index 0000000000..7ea0fa3146 --- /dev/null +++ b/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr @@ -0,0 +1,15 @@ +error: memory leaked: ALLOC (C heap, size: 0, align: 64), allocated here: + --> $DIR/posix_memalign_size_zero_leak.rs:LL:CC + | +LL | let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: BACKTRACE: + = note: inside `main` at $DIR/posix_memalign_size_zero_leak.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check + +error: aborting due to 1 previous error + diff --git a/tests/pass-dep/libc/libc-mem.rs b/tests/pass-dep/libc/libc-mem.rs index 5df3ace749..b36fb436b5 100644 --- a/tests/pass-dep/libc/libc-mem.rs +++ b/tests/pass-dep/libc/libc-mem.rs @@ -190,11 +190,10 @@ fn test_memalign() { let align = 64; let size = 0; assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0); - // We are not required to return null if size == 0, but we currently do. - // It's fine to remove this assert if we start returning non-null pointers. - assert!(ptr.is_null()); + // Non-null pointer is returned if size == 0. + // (This is not a guarantee, it just reflects our current behavior.) + assert!(!ptr.is_null()); assert!(ptr.is_aligned_to(align)); - // Regardless of what we return, it must be `free`able. libc::free(ptr); } From 5649c0207b8d597905c45147de47c06f209aeec4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 May 2024 10:09:45 +0200 Subject: [PATCH 202/208] organize float intrinsic implementations a bit --- src/intrinsics/mod.rs | 194 ++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 102 deletions(-) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index cf5d991381..7344846d6d 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -167,6 +167,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // This is a "bitwise" operation, so there's no NaN non-determinism. this.write_scalar(Scalar::from_f64(f.abs()), dest)?; } + "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => { let [f] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; @@ -182,6 +183,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => { + let [f] = check_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + let mode = match intrinsic_name { + "floorf64" => Round::TowardNegative, + "ceilf64" => Round::TowardPositive, + "truncf64" => Round::TowardZero, + "roundf64" => Round::NearestTiesToAway, + "rintf64" => Round::NearestTiesToEven, + _ => bug!(), + }; + let res = f.round_to_integral(mode).value; + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] | "sinf32" | "cosf32" @@ -211,22 +228,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - - "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => { - let [f] = check_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - let mode = match intrinsic_name { - "floorf64" => Round::TowardNegative, - "ceilf64" => Round::TowardPositive, - "truncf64" => Round::TowardZero, - "roundf64" => Round::NearestTiesToAway, - "rintf64" => Round::NearestTiesToEven, - _ => bug!(), - }; - let res = f.round_to_integral(mode).value; - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } #[rustfmt::skip] | "sinf64" | "cosf64" @@ -256,84 +257,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - #[rustfmt::skip] - | "fadd_algebraic" - | "fsub_algebraic" - | "fmul_algebraic" - | "fdiv_algebraic" - | "frem_algebraic" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_algebraic" => mir::BinOp::Add, - "fsub_algebraic" => mir::BinOp::Sub, - "fmul_algebraic" => mir::BinOp::Mul, - "fdiv_algebraic" => mir::BinOp::Div, - "frem_algebraic" => mir::BinOp::Rem, - _ => bug!(), - }; - let res = this.wrapping_binary_op(op, &a, &b)?; - // `wrapping_binary_op` already called `generate_nan` if necessary. - this.write_immediate(*res, dest)?; - } - - #[rustfmt::skip] - | "fadd_fast" - | "fsub_fast" - | "fmul_fast" - | "fdiv_fast" - | "frem_fast" - => { - let [a, b] = check_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_fast" => mir::BinOp::Add, - "fsub_fast" => mir::BinOp::Sub, - "fmul_fast" => mir::BinOp::Mul, - "fdiv_fast" => mir::BinOp::Div, - "frem_fast" => mir::BinOp::Rem, - _ => bug!(), - }; - let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { - let ty::Float(fty) = x.layout.ty.kind() else { - bug!("float_finite: non-float input type {}", x.layout.ty) - }; - Ok(match fty { - FloatTy::F16 => unimplemented!("f16_f128"), - FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), - FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), - FloatTy::F128 => unimplemented!("f16_f128"), - }) - }; - match (float_finite(&a)?, float_finite(&b)?) { - (false, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", - ), - (false, _) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", - ), - (_, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", - ), - _ => {} - } - let res = this.wrapping_binary_op(op, &a, &b)?; - if !float_finite(&res)? { - throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); - } - // This cannot be a NaN so we also don't have to apply any non-determinism. - // (Also, `wrapping_binary_op` already called `generate_nan` if needed.) - this.write_immediate(*res, dest)?; - } - #[rustfmt::skip] - | "minnumf32" - | "maxnumf32" - | "copysignf32" - => { + "minnumf32" | "maxnumf32" | "copysignf32" => { let [a, b] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f32()?; let b = this.read_scalar(b)?.to_f32()?; @@ -345,12 +270,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; this.write_scalar(Scalar::from_f32(res), dest)?; } - - #[rustfmt::skip] - | "minnumf64" - | "maxnumf64" - | "copysignf64" - => { + "minnumf64" | "maxnumf64" | "copysignf64" => { let [a, b] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f64()?; let b = this.read_scalar(b)?.to_f64()?; @@ -373,7 +293,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[a, b, c]); this.write_scalar(res, dest)?; } - "fmaf64" => { let [a, b, c] = check_arg_count(args)?; let a = this.read_scalar(a)?.to_f64()?; @@ -394,7 +313,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f1, f2]); this.write_scalar(res, dest)?; } - "powf64" => { let [f1, f2] = check_arg_count(args)?; let f1 = this.read_scalar(f1)?.to_f64()?; @@ -414,7 +332,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } - "powif64" => { let [f, i] = check_arg_count(args)?; let f = this.read_scalar(f)?.to_f64()?; @@ -425,6 +342,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(res, dest)?; } + #[rustfmt::skip] + | "fadd_algebraic" + | "fsub_algebraic" + | "fmul_algebraic" + | "fdiv_algebraic" + | "frem_algebraic" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_algebraic" => mir::BinOp::Add, + "fsub_algebraic" => mir::BinOp::Sub, + "fmul_algebraic" => mir::BinOp::Mul, + "fdiv_algebraic" => mir::BinOp::Div, + "frem_algebraic" => mir::BinOp::Rem, + _ => bug!(), + }; + let res = this.wrapping_binary_op(op, &a, &b)?; + // `wrapping_binary_op` already called `generate_nan` if necessary. + this.write_immediate(*res, dest)?; + } + + #[rustfmt::skip] + | "fadd_fast" + | "fsub_fast" + | "fmul_fast" + | "fdiv_fast" + | "frem_fast" + => { + let [a, b] = check_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> { + let ty::Float(fty) = x.layout.ty.kind() else { + bug!("float_finite: non-float input type {}", x.layout.ty) + }; + Ok(match fty { + FloatTy::F16 => unimplemented!("f16_f128"), + FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), + FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), + FloatTy::F128 => unimplemented!("f16_f128"), + }) + }; + match (float_finite(&a)?, float_finite(&b)?) { + (false, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", + ), + (false, _) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", + ), + (_, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", + ), + _ => {} + } + let res = this.wrapping_binary_op(op, &a, &b)?; + if !float_finite(&res)? { + throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); + } + // This cannot be a NaN so we also don't have to apply any non-determinism. + // (Also, `wrapping_binary_op` already called `generate_nan` if needed.) + this.write_immediate(*res, dest)?; + } + "float_to_int_unchecked" => { let [val] = check_arg_count(args)?; let val = this.read_immediate(val)?; From 0a7ea5b912932de3d639752f1fd4953ff5a1b86f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 May 2024 10:21:00 +0200 Subject: [PATCH 203/208] merge float tests into one --- tests/pass/float.rs | 67 +++++++++++++++++++ tests/pass/float_fast_math.rs | 34 ---------- tests/pass/intrinsics/float_algebraic_math.rs | 32 --------- 3 files changed, 67 insertions(+), 66 deletions(-) delete mode 100644 tests/pass/float_fast_math.rs delete mode 100644 tests/pass/intrinsics/float_algebraic_math.rs diff --git a/tests/pass/float.rs b/tests/pass/float.rs index 1bb44d56bf..8aea9b3e6f 100644 --- a/tests/pass/float.rs +++ b/tests/pass/float.rs @@ -1,5 +1,6 @@ #![feature(stmt_expr_attributes)] #![feature(float_gamma)] +#![feature(core_intrinsics)] #![allow(arithmetic_overflow)] use std::fmt::Debug; @@ -22,6 +23,8 @@ fn main() { rounding(); mul_add(); libm(); + test_fast(); + test_algebraic(); } // Helper function to avoid promotion so that this tests "run-time" casts, not CTFE. @@ -751,3 +754,67 @@ pub fn libm() { assert_approx_eq!(val, (2.0 * f64::consts::PI.sqrt()).ln()); assert_eq!(sign, -1); } + +fn test_fast() { + use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; + + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } + } + + #[inline(never)] + pub fn test_operations_f32(a: f32, b: f32) { + // make sure they all map to the correct operation + unsafe { + assert_eq!(fadd_fast(a, b), a + b); + assert_eq!(fsub_fast(a, b), a - b); + assert_eq!(fmul_fast(a, b), a * b); + assert_eq!(fdiv_fast(a, b), a / b); + assert_eq!(frem_fast(a, b), a % b); + } + } + + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f32(11., 2.); + test_operations_f32(10., 15.); +} + +fn test_algebraic() { + use std::intrinsics::{ + fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic, + }; + + #[inline(never)] + pub fn test_operations_f64(a: f64, b: f64) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); + } + + #[inline(never)] + pub fn test_operations_f32(a: f32, b: f32) { + // make sure they all map to the correct operation + assert_eq!(fadd_algebraic(a, b), a + b); + assert_eq!(fsub_algebraic(a, b), a - b); + assert_eq!(fmul_algebraic(a, b), a * b); + assert_eq!(fdiv_algebraic(a, b), a / b); + assert_eq!(frem_algebraic(a, b), a % b); + } + + test_operations_f64(1., 2.); + test_operations_f64(10., 5.); + test_operations_f32(11., 2.); + test_operations_f32(10., 15.); +} diff --git a/tests/pass/float_fast_math.rs b/tests/pass/float_fast_math.rs deleted file mode 100644 index 52d985667d..0000000000 --- a/tests/pass/float_fast_math.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![feature(core_intrinsics)] - -use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast}; - -#[inline(never)] -pub fn test_operations_f64(a: f64, b: f64) { - // make sure they all map to the correct operation - unsafe { - assert_eq!(fadd_fast(a, b), a + b); - assert_eq!(fsub_fast(a, b), a - b); - assert_eq!(fmul_fast(a, b), a * b); - assert_eq!(fdiv_fast(a, b), a / b); - assert_eq!(frem_fast(a, b), a % b); - } -} - -#[inline(never)] -pub fn test_operations_f32(a: f32, b: f32) { - // make sure they all map to the correct operation - unsafe { - assert_eq!(fadd_fast(a, b), a + b); - assert_eq!(fsub_fast(a, b), a - b); - assert_eq!(fmul_fast(a, b), a * b); - assert_eq!(fdiv_fast(a, b), a / b); - assert_eq!(frem_fast(a, b), a % b); - } -} - -fn main() { - test_operations_f64(1., 2.); - test_operations_f64(10., 5.); - test_operations_f32(11., 2.); - test_operations_f32(10., 15.); -} diff --git a/tests/pass/intrinsics/float_algebraic_math.rs b/tests/pass/intrinsics/float_algebraic_math.rs deleted file mode 100644 index f6f083f7b5..0000000000 --- a/tests/pass/intrinsics/float_algebraic_math.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![feature(core_intrinsics)] - -use std::intrinsics::{ - fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic, -}; - -#[inline(never)] -pub fn test_operations_f64(a: f64, b: f64) { - // make sure they all map to the correct operation - assert_eq!(fadd_algebraic(a, b), a + b); - assert_eq!(fsub_algebraic(a, b), a - b); - assert_eq!(fmul_algebraic(a, b), a * b); - assert_eq!(fdiv_algebraic(a, b), a / b); - assert_eq!(frem_algebraic(a, b), a % b); -} - -#[inline(never)] -pub fn test_operations_f32(a: f32, b: f32) { - // make sure they all map to the correct operation - assert_eq!(fadd_algebraic(a, b), a + b); - assert_eq!(fsub_algebraic(a, b), a - b); - assert_eq!(fmul_algebraic(a, b), a * b); - assert_eq!(fdiv_algebraic(a, b), a / b); - assert_eq!(frem_algebraic(a, b), a % b); -} - -fn main() { - test_operations_f64(1., 2.); - test_operations_f64(10., 5.); - test_operations_f32(11., 2.); - test_operations_f32(10., 15.); -} From 10a829446d32deb9902212110090792120359d01 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 May 2024 10:22:59 +0200 Subject: [PATCH 204/208] merge two integer tests --- tests/pass/integer-ops.rs | 61 +++++++++++++++++++++++++++++++++++++++ tests/pass/integers.rs | 58 ------------------------------------- 2 files changed, 61 insertions(+), 58 deletions(-) delete mode 100644 tests/pass/integers.rs diff --git a/tests/pass/integer-ops.rs b/tests/pass/integer-ops.rs index 0ec1f8e9c6..3f8ac34e7d 100644 --- a/tests/pass/integer-ops.rs +++ b/tests/pass/integer-ops.rs @@ -1,7 +1,64 @@ //@compile-flags: -Coverflow-checks=off #![allow(arithmetic_overflow)] +fn basic() { + fn ret() -> i64 { + 1 + } + + fn neg() -> i64 { + -1 + } + + fn add() -> i64 { + 1 + 2 + } + + fn indirect_add() -> i64 { + let x = 1; + let y = 2; + x + y + } + + fn arith() -> i32 { + 3 * 3 + 4 * 4 + } + + fn match_int() -> i16 { + let n = 2; + match n { + 0 => 0, + 1 => 10, + 2 => 20, + 3 => 30, + _ => 100, + } + } + + fn match_int_range() -> i64 { + let n = 42; + match n { + 0..=9 => 0, + 10..=19 => 1, + 20..=29 => 2, + 30..=39 => 3, + 40..=42 => 4, + _ => 5, + } + } + + assert_eq!(ret(), 1); + assert_eq!(neg(), -1); + assert_eq!(add(), 3); + assert_eq!(indirect_add(), 3); + assert_eq!(arith(), 5 * 5); + assert_eq!(match_int(), 20); + assert_eq!(match_int_range(), 4); +} + pub fn main() { + basic(); + // This tests that we do (not) do sign extension properly when loading integers assert_eq!(u32::MAX as i64, 4294967295); assert_eq!(i32::MIN as i64, -2147483648); @@ -152,6 +209,10 @@ pub fn main() { assert_eq!(5i32.overflowing_mul(2), (10, false)); assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true)); + assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true)); + assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true)); + assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true)); + assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true)); assert_eq!(5i32.overflowing_div(2), (2, false)); assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true)); diff --git a/tests/pass/integers.rs b/tests/pass/integers.rs deleted file mode 100644 index c04c6921f3..0000000000 --- a/tests/pass/integers.rs +++ /dev/null @@ -1,58 +0,0 @@ -fn ret() -> i64 { - 1 -} - -fn neg() -> i64 { - -1 -} - -fn add() -> i64 { - 1 + 2 -} - -fn indirect_add() -> i64 { - let x = 1; - let y = 2; - x + y -} - -fn arith() -> i32 { - 3 * 3 + 4 * 4 -} - -fn match_int() -> i16 { - let n = 2; - match n { - 0 => 0, - 1 => 10, - 2 => 20, - 3 => 30, - _ => 100, - } -} - -fn match_int_range() -> i64 { - let n = 42; - match n { - 0..=9 => 0, - 10..=19 => 1, - 20..=29 => 2, - 30..=39 => 3, - 40..=42 => 4, - _ => 5, - } -} - -fn main() { - assert_eq!(ret(), 1); - assert_eq!(neg(), -1); - assert_eq!(add(), 3); - assert_eq!(indirect_add(), 3); - assert_eq!(arith(), 5 * 5); - assert_eq!(match_int(), 20); - assert_eq!(match_int_range(), 4); - assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true)); - assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true)); - assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true)); - assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true)); -} From 7c244dddd73070898fc456b1af2f5af46bb6ad38 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 12 May 2024 15:44:17 +0200 Subject: [PATCH 205/208] Preparing for merge from rustc --- rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-version b/rust-version index 3636c856d0..603417b77e 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -ef15976387ad9c1cdceaabf469e0cf35f5852f6d +b71fa82d786ae1b5866510f1b3a7e5b7e1890e4c From a18a0f86c458ad93b3616e3e499fc60a4f190ce8 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 11 May 2024 15:24:03 -0400 Subject: [PATCH 206/208] Don't print unnecessary sysroot messages --- cargo-miri/Cargo.lock | 4 ++-- cargo-miri/Cargo.toml | 2 +- cargo-miri/src/setup.rs | 50 ++++++++++++++++++++++++++--------------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/cargo-miri/Cargo.lock b/cargo-miri/Cargo.lock index b8ead46024..7d965dce07 100644 --- a/cargo-miri/Cargo.lock +++ b/cargo-miri/Cargo.lock @@ -178,9 +178,9 @@ dependencies = [ [[package]] name = "rustc-build-sysroot" -version = "0.4.7" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0" +checksum = "de6077473f0c46779b49e4587a81f1b8919e0ec26630409ecfda0ba3259efb43" dependencies = [ "anyhow", "rustc_version", diff --git a/cargo-miri/Cargo.toml b/cargo-miri/Cargo.toml index b16068b6d1..a68854de62 100644 --- a/cargo-miri/Cargo.toml +++ b/cargo-miri/Cargo.toml @@ -18,7 +18,7 @@ directories = "5" rustc_version = "0.4" serde_json = "1.0.40" cargo_metadata = "0.18.0" -rustc-build-sysroot = "0.4.6" +rustc-build-sysroot = "0.5.0" # Enable some feature flags that dev-dependencies need but dependencies # do not. This makes `./miri install` after `./miri build` faster. diff --git a/cargo-miri/src/setup.rs b/cargo-miri/src/setup.rs index 9a58e6fa01..508d304536 100644 --- a/cargo-miri/src/setup.rs +++ b/cargo-miri/src/setup.rs @@ -6,7 +6,7 @@ use std::fmt::Write; use std::path::PathBuf; use std::process::{self, Command}; -use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig}; +use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig, SysrootStatus}; use rustc_version::VersionMeta; use crate::util::*; @@ -137,32 +137,52 @@ pub fn setup( // not apply `RUSTFLAGS` to the sysroot either. let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"]; - // Do the build. - if print_sysroot || quiet { - // Be silent. - } else { + let notify = || { let mut msg = String::new(); write!(msg, "Preparing a sysroot for Miri (target: {target})").unwrap(); if verbose > 0 { write!(msg, " in {}", sysroot_dir.display()).unwrap(); } write!(msg, "...").unwrap(); - if only_setup { + + if print_sysroot || quiet { + // Be silent. + } else if only_setup { // We want to be explicit. eprintln!("{msg}"); } else { // We want to be quiet, but still let the user know that something is happening. eprint!("{msg} "); } - } - SysrootBuilder::new(&sysroot_dir, target) + }; + + // Do the build. + let status = SysrootBuilder::new(&sysroot_dir, target) .build_mode(BuildMode::Check) .rustc_version(rustc_version.clone()) .sysroot_config(sysroot_config) .rustflags(rustflags) .cargo(cargo_cmd) - .build_from_source(&rust_src) - .unwrap_or_else(|err| { + .when_build_required(notify) + .build_from_source(&rust_src); + match status { + Ok(SysrootStatus::AlreadyCached) => + if only_setup && !(print_sysroot || quiet) { + eprintln!( + "A sysroot for Miri is already available in `{}`.", + sysroot_dir.display() + ); + }, + Ok(SysrootStatus::SysrootBuilt) => { + if print_sysroot || quiet { + // Be silent. + } else if only_setup { + eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display()); + } else { + eprintln!("done"); + } + } + Err(err) => if print_sysroot { show_error!("failed to build sysroot") } else if only_setup { @@ -171,15 +191,9 @@ pub fn setup( show_error!( "failed to build sysroot; run `cargo miri setup` to see the error details" ) - } - }); - if print_sysroot || quiet { - // Be silent. - } else if only_setup { - eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display()); - } else { - eprintln!("done"); + }, } + if print_sysroot { // Print just the sysroot and nothing else to stdout; this way we do not need any escaping. println!("{}", sysroot_dir.display()); From 20a0257be6ac3a567274229357ab35719d24c6b6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 5 May 2024 22:50:22 +0000 Subject: [PATCH 207/208] further illumos/solaris support. fixing part of `miri test alloc/hashmap`. --- ci/ci.sh | 4 ++-- src/shims/extern_static.rs | 4 ++++ src/shims/unix/foreign_items.rs | 3 ++- tests/fail/environ-gets-deallocated.rs | 7 ++++++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ci/ci.sh b/ci/ci.sh index f5fbb05d89..6292d1033b 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -148,8 +148,8 @@ case $HOST_TARGET in BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus - TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random - TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random + TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random env + TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random env TEST_TARGET=aarch64-linux-android run_tests_minimal $VERY_BASIC hello panic/panic TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm diff --git a/src/shims/extern_static.rs b/src/shims/extern_static.rs index c3c7ef7c1f..12fede39e2 100644 --- a/src/shims/extern_static.rs +++ b/src/shims/extern_static.rs @@ -76,6 +76,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { Self::null_ptr_extern_statics(this, &["bsd_signal"])?; Self::weak_symbol_extern_statics(this, &["signal"])?; } + "solaris" | "illumos" => { + let environ = this.machine.env_vars.unix().environ(); + Self::add_extern_static(this, "environ", environ); + } "windows" => { // "_tls_used" // This is some obscure hack that is part of the Windows TLS story. It's a `u8`. diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index b5165548d1..5434951d9e 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -714,8 +714,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_int(super::UID, dest)?; } - "getpwuid_r" + "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => { + // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish let [uid, pwd, buf, buflen, result] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; diff --git a/tests/fail/environ-gets-deallocated.rs b/tests/fail/environ-gets-deallocated.rs index 6dcf1fdb1c..08545a7256 100644 --- a/tests/fail/environ-gets-deallocated.rs +++ b/tests/fail/environ-gets-deallocated.rs @@ -1,6 +1,11 @@ //@ignore-target-windows: Windows does not have a global environ list that the program can access directly -#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "solaris", + target_os = "illumos" +))] fn get_environ() -> *const *const u8 { extern "C" { static mut environ: *const *const u8; From 792876b52d66c7cbe76275946255ec60381dfe5d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 13 May 2024 08:21:05 +0200 Subject: [PATCH 208/208] intrinsics: just panic when they get used incorrectly --- src/intrinsics/atomic.rs | 66 ++++++++++++++++++++-------------------- src/shims/x86/avx2.rs | 2 +- src/shims/x86/mod.rs | 4 +-- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/intrinsics/atomic.rs b/src/intrinsics/atomic.rs index 40f6b8a64e..0c212c45db 100644 --- a/src/intrinsics/atomic.rs +++ b/src/intrinsics/atomic.rs @@ -25,93 +25,93 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect(); - fn read_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicReadOrd> { - Ok(match ord { + fn read_ord(ord: &str) -> AtomicReadOrd { + match ord { "seqcst" => AtomicReadOrd::SeqCst, "acquire" => AtomicReadOrd::Acquire, "relaxed" => AtomicReadOrd::Relaxed, - _ => throw_unsup_format!("unsupported read ordering `{ord}`"), - }) + _ => panic!("invalid read ordering `{ord}`"), + } } - fn write_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicWriteOrd> { - Ok(match ord { + fn write_ord(ord: &str) -> AtomicWriteOrd { + match ord { "seqcst" => AtomicWriteOrd::SeqCst, "release" => AtomicWriteOrd::Release, "relaxed" => AtomicWriteOrd::Relaxed, - _ => throw_unsup_format!("unsupported write ordering `{ord}`"), - }) + _ => panic!("invalid write ordering `{ord}`"), + } } - fn rw_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicRwOrd> { - Ok(match ord { + fn rw_ord(ord: &str) -> AtomicRwOrd { + match ord { "seqcst" => AtomicRwOrd::SeqCst, "acqrel" => AtomicRwOrd::AcqRel, "acquire" => AtomicRwOrd::Acquire, "release" => AtomicRwOrd::Release, "relaxed" => AtomicRwOrd::Relaxed, - _ => throw_unsup_format!("unsupported read-write ordering `{ord}`"), - }) + _ => panic!("invalid read-write ordering `{ord}`"), + } } - fn fence_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicFenceOrd> { - Ok(match ord { + fn fence_ord(ord: &str) -> AtomicFenceOrd { + match ord { "seqcst" => AtomicFenceOrd::SeqCst, "acqrel" => AtomicFenceOrd::AcqRel, "acquire" => AtomicFenceOrd::Acquire, "release" => AtomicFenceOrd::Release, - _ => throw_unsup_format!("unsupported fence ordering `{ord}`"), - }) + _ => panic!("invalid fence ordering `{ord}`"), + } } match &*intrinsic_structure { - ["load", ord] => this.atomic_load(args, dest, read_ord(ord)?)?, - ["store", ord] => this.atomic_store(args, write_ord(ord)?)?, + ["load", ord] => this.atomic_load(args, dest, read_ord(ord))?, + ["store", ord] => this.atomic_store(args, write_ord(ord))?, - ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord)?)?, - ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord)?)?, + ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?, + ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord))?, - ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord)?)?, + ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord))?, ["cxchg", ord1, ord2] => - this.atomic_compare_exchange(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?, + this.atomic_compare_exchange(args, dest, rw_ord(ord1), read_ord(ord2))?, ["cxchgweak", ord1, ord2] => - this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?, + this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1), read_ord(ord2))?, ["or", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?, ["xor", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?, ["and", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?, ["nand", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?, ["xadd", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?, ["xsub", ord] => - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord)?)?, + this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?, ["min", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; } ["umin", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; } ["max", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; } ["umax", ord] => { // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?; + this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; } _ => return Ok(EmulateItemResult::NotSupported), diff --git a/src/shims/x86/avx2.rs b/src/shims/x86/avx2.rs index bbde5b4958..adecf7b892 100644 --- a/src/shims/x86/avx2.rs +++ b/src/shims/x86/avx2.rs @@ -81,7 +81,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let scale = this.read_scalar(scale)?.to_i8()?; if !matches!(scale, 1 | 2 | 4 | 8) { - throw_unsup_format!("invalid gather scale {scale}"); + panic!("invalid gather scale {scale}"); } let scale = i64::from(scale); diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index e519fa5508..58d6db1886 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -200,7 +200,7 @@ impl FloatBinOp { ) -> InterpResult<'tcx, Self> { // Only bits 0..=4 are used, remaining should be zero. if imm & !0b1_1111 != 0 { - throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}"); + panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}"); } // Bit 4 specifies whether the operation is quiet or signaling, which // we do not care in Miri. @@ -683,7 +683,7 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R // SSE status register. Since we do not support modifying it from // Miri (or Rust), we assume it to be at its default mode (round-to-nearest). 0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven), - rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"), + rounding => panic!("invalid rounding mode 0x{rounding:02x}"), } }