1
Fork 0

Rollup merge of #136452 - RalfJung:miri-sync, r=RalfJung

Miri subtree update

r? `@ghost`

Unblocks https://github.com/rust-lang/rust/pull/122408 from the Miri side
This commit is contained in:
Matthias Krüger 2025-02-02 23:06:58 +01:00 committed by GitHub
commit a8055f944f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
61 changed files with 1477 additions and 568 deletions

View file

@ -43,7 +43,7 @@ dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@ -567,7 +567,7 @@ dependencies = [
"termize",
"tokio",
"toml 0.7.8",
"ui_test",
"ui_test 0.26.5",
"walkdir",
]
@ -1442,7 +1442,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets 0.52.6",
]
[[package]]
@ -2342,18 +2354,18 @@ dependencies = [
"chrono-tz",
"colored",
"directories",
"getrandom",
"getrandom 0.3.1",
"libc",
"libffi",
"libloading",
"measureme",
"rand",
"rand 0.9.0",
"regex",
"rustc_version",
"smallvec",
"tempfile",
"tikv-jemalloc-sys",
"ui_test",
"ui_test 0.28.0",
"windows-sys 0.52.0",
]
@ -2782,7 +2794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared 0.10.0",
"rand",
"rand 0.8.5",
]
[[package]]
@ -2792,7 +2804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared 0.11.3",
"rand",
"rand 0.8.5",
]
[[package]]
@ -2860,7 +2872,7 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@ -2978,8 +2990,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.0",
"zerocopy 0.8.14",
]
[[package]]
@ -2989,7 +3012,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.0",
]
[[package]]
@ -2998,7 +3031,17 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
name = "rand_core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
dependencies = [
"getrandom 0.3.1",
"zerocopy 0.8.14",
]
[[package]]
@ -3007,7 +3050,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
dependencies = [
"rand_core",
"rand_core 0.6.4",
]
[[package]]
@ -3045,7 +3088,7 @@ version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"getrandom 0.2.15",
"libredox",
"thiserror 1.0.69",
]
@ -3283,7 +3326,7 @@ name = "rustc_abi"
version = "0.0.0"
dependencies = [
"bitflags",
"rand",
"rand 0.8.5",
"rand_xoshiro",
"rustc_data_structures",
"rustc_feature",
@ -3897,7 +3940,7 @@ dependencies = [
name = "rustc_incremental"
version = "0.0.0"
dependencies = [
"rand",
"rand 0.8.5",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
@ -5218,7 +5261,7 @@ checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
dependencies = [
"cfg-if",
"fastrand",
"getrandom",
"getrandom 0.2.15",
"once_cell",
"rustix",
"windows-sys 0.59.0",
@ -5281,8 +5324,8 @@ version = "0.1.0"
dependencies = [
"indicatif",
"num",
"rand",
"rand_chacha",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rayon",
]
@ -5602,7 +5645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"rand",
"rand 0.8.5",
"static_assertions",
]
@ -5662,6 +5705,32 @@ dependencies = [
"spanned",
]
[[package]]
name = "ui_test"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7484683d60d50ca1d1b6433c3dbf6c5ad71d20387acdcfb16fe79573f3fba576"
dependencies = [
"annotate-snippets 0.11.5",
"anyhow",
"bstr",
"cargo-platform",
"cargo_metadata 0.18.1",
"color-eyre",
"colored",
"comma",
"crossbeam-channel",
"indicatif",
"levenshtein",
"prettydiff",
"regex",
"rustc_version",
"rustfix",
"serde",
"serde_json",
"spanned",
]
[[package]]
name = "unic-langid"
version = "0.9.5"
@ -5843,7 +5912,7 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744018581f9a3454a9e15beb8a33b017183f1e7c0cd170232a2d1453b23a51c4"
dependencies = [
"getrandom",
"getrandom 0.2.15",
]
[[package]]
@ -5880,6 +5949,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasi-preview1-component-adapter-provider"
version = "29.0.1"
@ -6475,6 +6553,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab703352da6a72f35c39a533526393725640575bb211f61987a2748323ad956"
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]
[[package]]
name = "wit-component"
version = "0.223.0"
@ -6584,7 +6671,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
dependencies = [
"zerocopy-derive 0.8.14",
]
[[package]]
@ -6598,6 +6694,17 @@ dependencies = [
"syn 2.0.96",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.96",
]
[[package]]
name = "zerofrom"
version = "0.1.5"

View file

@ -351,7 +351,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets 0.52.6",
]
[[package]]
@ -529,12 +541,12 @@ dependencies = [
"chrono-tz",
"colored",
"directories",
"getrandom",
"getrandom 0.3.1",
"libc",
"libffi",
"libloading",
"measureme",
"rand",
"rand 0.9.0",
"regex",
"rustc_version",
"smallvec",
@ -662,7 +674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
"rand 0.8.5",
]
[[package]]
@ -692,7 +704,7 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
"zerocopy 0.7.35",
]
[[package]]
@ -729,19 +741,28 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha",
"rand_core",
"rand_core 0.9.0",
"zerocopy 0.8.14",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.9.0",
]
[[package]]
@ -749,8 +770,15 @@ name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rand_core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
dependencies = [
"getrandom",
"getrandom 0.3.1",
"zerocopy 0.8.14",
]
[[package]]
@ -768,7 +796,7 @@ version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom",
"getrandom 0.2.15",
"libredox",
"thiserror",
]
@ -1051,9 +1079,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ui_test"
version = "0.26.5"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83"
checksum = "7484683d60d50ca1d1b6433c3dbf6c5ad71d20387acdcfb16fe79573f3fba576"
dependencies = [
"annotate-snippets",
"anyhow",
@ -1105,6 +1133,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -1244,6 +1281,15 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
@ -1251,7 +1297,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468"
dependencies = [
"zerocopy-derive 0.8.14",
]
[[package]]
@ -1264,3 +1319,14 @@ dependencies = [
"quote",
"syn",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

View file

@ -18,8 +18,8 @@ test = false # we have no unit tests
doctest = false # and no doc tests
[dependencies]
getrandom = { version = "0.2", features = ["std"] }
rand = "0.8"
getrandom = { version = "0.3", features = ["std"] }
rand = "0.9"
smallvec = { version = "1.7", features = ["drain_filter"] }
aes = { version = "0.8.3", features = ["hazmat"] }
measureme = "11"
@ -47,8 +47,8 @@ windows-sys = { version = "0.52", features = [
] }
[dev-dependencies]
ui_test = "0.28.0"
colored = "2"
ui_test = "0.26.5"
rustc_version = "0.4"
regex = "1.5.5"
tempfile = "3"

View file

@ -14,9 +14,7 @@ function endgroup {
begingroup "Building Miri"
# Global configuration
# We are getting some odd linker warnings on macOS, make sure they do not fail the build.
# (See <https://github.com/rust-lang/rust/issues/136086>.)
export RUSTFLAGS="-D warnings -A linker-messages"
export RUSTFLAGS="-D warnings"
export CARGO_INCREMENTAL=0
export CARGO_EXTRA_FLAGS="--locked"

View file

@ -1 +1 @@
2f0ad2a71e4a4528bb80bcb24bf8fa4e50cb87c2
6dd75f0d6802f56564f5f9c947a85ded286d3986

View file

@ -217,7 +217,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// We have to pick a fresh address.
// Leave some space to the previous allocation, to give it some chance to be less aligned.
// We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
let slack = rng.gen_range(0..16);
let slack = rng.random_range(0..16);
// From next_base_addr + slack, round up to adjust for alignment.
let base_addr = global_state
.next_base_addr

View file

@ -58,7 +58,7 @@ impl ReusePool {
// 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) {
if kind == MemoryKind::Stack || !rng.random_bool(self.address_reuse_rate) {
return;
}
let clock = clock();
@ -88,10 +88,10 @@ impl ReusePool {
thread: ThreadId,
) -> Option<(u64, Option<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) {
if kind == MemoryKind::Stack || !rng.random_bool(self.address_reuse_rate) {
return None;
}
let cross_thread_reuse = rng.gen_bool(self.address_reuse_cross_thread_rate);
let cross_thread_reuse = rng.random_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
@ -118,7 +118,7 @@ impl ReusePool {
return None;
}
// Pick a random element with the desired size.
let idx = rng.gen_range(begin..end);
let idx = rng.random_range(begin..end);
// Remove it from the pool and return.
let (chosen_addr, chosen_size, chosen_thread, clock) = subpool.remove(idx);
debug_assert!(chosen_size >= size && chosen_addr % align.bytes() == 0);

View file

@ -721,8 +721,8 @@ fn main() {
// Ensure we have parallelism for many-seeds mode.
if many_seeds.is_some() && !rustc_args.iter().any(|arg| arg.starts_with("-Zthreads=")) {
// Clamp to 8 threads; things get a lot less efficient beyond that due to lock contention.
let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(8);
// Clamp to 10 threads; things get a lot less efficient beyond that due to lock contention.
let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(10);
rustc_args.push(format!("-Zthreads={threads}"));
}
let many_seeds =

View file

@ -865,7 +865,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this);
let cause = match kind {
RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
RetagKind::TwoPhase => RetagCause::TwoPhase,
RetagKind::FnEntry => unreachable!(),
RetagKind::Raw | RetagKind::Default => RetagCause::Normal,
};
@ -880,7 +880,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
let retag_cause = match kind {
RetagKind::TwoPhase { .. } => unreachable!(), // can only happen in `retag_ptr_value`
RetagKind::TwoPhase => unreachable!(), // can only happen in `retag_ptr_value`
RetagKind::FnEntry => RetagCause::FnEntry,
RetagKind::Default | RetagKind::Raw => RetagCause::Normal,
};
@ -904,10 +904,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
new_perm: NewPermission,
) -> InterpResult<'tcx> {
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
let val = self.ecx.sb_retag_reference(&val, new_perm, RetagInfo {
cause: self.retag_cause,
in_field: self.in_field,
})?;
let val = self.ecx.sb_retag_reference(
&val,
new_perm,
RetagInfo { cause: self.retag_cause, in_field: self.in_field },
)?;
self.ecx.write_immediate(*val, place)?;
interp_ok(())
}
@ -996,10 +997,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
access: Some(AccessKind::Write),
protector: Some(ProtectorKind::StrongProtector),
};
this.sb_retag_place(place, new_perm, RetagInfo {
cause: RetagCause::InPlaceFnPassing,
in_field: false,
})
this.sb_retag_place(
place,
new_perm,
RetagInfo { cause: RetagCause::InPlaceFnPassing, in_field: false },
)
}
/// Mark the given tag as exposed. It was found on a pointer with the given AllocId.

View file

@ -379,14 +379,18 @@ pub mod diagnostics {
use super::*;
impl fmt::Display for PermissionPriv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", match self {
ReservedFrz { conflicted: false } => "Reserved",
ReservedFrz { conflicted: true } => "Reserved (conflicted)",
ReservedIM => "Reserved (interior mutable)",
Active => "Active",
Frozen => "Frozen",
Disabled => "Disabled",
})
write!(
f,
"{}",
match self {
ReservedFrz { conflicted: false } => "Reserved",
ReservedFrz { conflicted: true } => "Reserved (conflicted)",
ReservedIM => "Reserved (interior mutable)",
Active => "Active",
Frozen => "Frozen",
Disabled => "Disabled",
}
)
}
}

View file

@ -581,15 +581,18 @@ impl Tree {
let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span);
// name the root so that all allocations contain one named pointer
debug_info.add_name("root of the allocation");
nodes.insert(root_idx, Node {
tag: root_tag,
parent: None,
children: SmallVec::default(),
default_initial_perm: root_default_perm,
// The root may never be skipped, all accesses will be local.
default_initial_idempotent_foreign_access: IdempotentForeignAccess::None,
debug_info,
});
nodes.insert(
root_idx,
Node {
tag: root_tag,
parent: None,
children: SmallVec::default(),
default_initial_perm: root_default_perm,
// The root may never be skipped, all accesses will be local.
default_initial_idempotent_foreign_access: IdempotentForeignAccess::None,
debug_info,
},
);
nodes
};
let rperms = {
@ -624,14 +627,17 @@ impl<'tcx> Tree {
let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
let strongest_idempotent = default_initial_perm.strongest_idempotent_foreign_access(prot);
// Create the node
self.nodes.insert(idx, Node {
tag: new_tag,
parent: Some(parent_idx),
children: SmallVec::default(),
default_initial_perm,
default_initial_idempotent_foreign_access: strongest_idempotent,
debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span),
});
self.nodes.insert(
idx,
Node {
tag: new_tag,
parent: Some(parent_idx),
children: SmallVec::default(),
default_initial_perm,
default_initial_idempotent_foreign_access: strongest_idempotent,
debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span),
},
);
// Register new_tag as a child of parent_tag
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
// Initialize perms

View file

@ -830,7 +830,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate;
let cmpxchg_success = eq.to_scalar().to_bool()?
&& if can_fail_spuriously {
this.machine.rng.get_mut().gen_bool(success_rate)
this.machine.rng.get_mut().random_bool(success_rate)
} else {
true
};

View file

@ -128,7 +128,7 @@ struct Condvar {
/// The futex state.
#[derive(Default, Debug)]
struct Futex {
waiters: VecDeque<FutexWaiter>,
waiters: Vec<FutexWaiter>,
/// Tracks the happens-before relationship
/// between a futex-wake and a futex-wait
/// during a non-spurious wake event.
@ -140,6 +140,12 @@ struct Futex {
#[derive(Default, Clone)]
pub struct FutexRef(Rc<RefCell<Futex>>);
impl FutexRef {
pub fn waiters(&self) -> usize {
self.0.borrow().waiters.len()
}
}
impl VisitProvenance for FutexRef {
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
// No provenance in `Futex`.
@ -728,25 +734,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(true)
}
/// Wait for the futex to be signaled, or a timeout.
/// On a signal, `retval_succ` is written to `dest`.
/// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
/// Wait for the futex to be signaled, or a timeout. Once the thread is
/// unblocked, `callback` is called with the unblock reason.
fn futex_wait(
&mut self,
futex_ref: FutexRef,
bitset: u32,
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
errno_timeout: IoError,
callback: DynUnblockCallback<'tcx>,
) {
let this = self.eval_context_mut();
let thread = this.active_thread();
let mut futex = futex_ref.0.borrow_mut();
let waiters = &mut futex.waiters;
assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
waiters.push_back(FutexWaiter { thread, bitset });
waiters.push(FutexWaiter { thread, bitset });
drop(futex);
this.block_thread(
@ -755,10 +757,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
futex_ref: FutexRef,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
errno_timeout: IoError,
callback: DynUnblockCallback<'tcx>,
}
|this, unblock: UnblockKind| {
match unblock {
@ -768,29 +767,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if let Some(data_race) = &this.machine.data_race {
data_race.acquire_clock(&futex.clock, &this.machine.threads);
}
// Write the return value.
this.write_scalar(retval_succ, &dest)?;
interp_ok(())
},
UnblockKind::TimedOut => {
// Remove the waiter from the futex.
let thread = this.active_thread();
let mut futex = futex_ref.0.borrow_mut();
futex.waiters.retain(|waiter| waiter.thread != thread);
// Set errno and write return value.
this.set_last_error(errno_timeout)?;
this.write_scalar(retval_timeout, &dest)?;
interp_ok(())
},
}
callback.call(this, unblock)
}
),
);
}
/// Wake up the first thread in the queue that matches any of the bits in the bitset.
/// Returns whether anything was woken.
fn futex_wake(&mut self, futex_ref: &FutexRef, bitset: u32) -> InterpResult<'tcx, bool> {
/// Wake up `count` of the threads in the queue that match any of the bits
/// in the bitset. Returns how many threads were woken.
fn futex_wake(
&mut self,
futex_ref: &FutexRef,
bitset: u32,
count: usize,
) -> InterpResult<'tcx, usize> {
let this = self.eval_context_mut();
let mut futex = futex_ref.0.borrow_mut();
let data_race = &this.machine.data_race;
@ -800,13 +799,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock));
}
// Wake up the first thread in the queue that matches any of the bits in the bitset.
let Some(i) = futex.waiters.iter().position(|w| w.bitset & bitset != 0) else {
return interp_ok(false);
};
let waiter = futex.waiters.remove(i).unwrap();
// Remove `count` of the threads in the queue that match any of the bits in the bitset.
// We collect all of them before unblocking because the unblock callback may access the
// futex state to retrieve the remaining number of waiters on macOS.
let waiters: Vec<_> =
futex.waiters.extract_if(.., |w| w.bitset & bitset != 0).take(count).collect();
drop(futex);
this.unblock_thread(waiter.thread, BlockReason::Futex)?;
interp_ok(true)
let woken = waiters.len();
for waiter in waiters {
this.unblock_thread(waiter.thread, BlockReason::Futex)?;
}
interp_ok(woken)
}
}

View file

@ -1138,7 +1138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
use rand::Rng as _;
let this = self.eval_context_mut();
if this.machine.rng.get_mut().gen_bool(this.machine.preemption_rate) {
if this.machine.rng.get_mut().random_bool(this.machine.preemption_rate) {
this.yield_active_thread();
}
}

View file

@ -558,15 +558,15 @@ where
match chars.next() {
Some('"') => {
cmd.extend(iter::repeat('\\').take(nslashes * 2 + 1));
cmd.extend(iter::repeat_n('\\', nslashes * 2 + 1));
cmd.push('"');
}
Some(c) => {
cmd.extend(iter::repeat('\\').take(nslashes));
cmd.extend(iter::repeat_n('\\', nslashes));
cmd.push(c);
}
None => {
cmd.extend(iter::repeat('\\').take(nslashes * 2));
cmd.extend(iter::repeat_n('\\', nslashes * 2));
break;
}
}

View file

@ -421,7 +421,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
if this.machine.communicate() {
// Fill the buffer using the host's rng.
getrandom::getrandom(&mut data)
getrandom::fill(&mut data)
.map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
} else {
let rng = this.machine.rng.get_mut();
@ -678,6 +678,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)
}
/// Helper function used inside shims of foreign functions to check that the target OS
/// is one of `target_oses`. It returns an error containing the `name` of the foreign function
/// in a message if this is not the case.
fn check_target_os(&self, target_oses: &[&str], name: Symbol) -> InterpResult<'tcx> {
let target_os = self.eval_context_ref().tcx.sess.target.os.as_ref();
if !target_oses.contains(&target_os) {
throw_unsup_format!("`{name}` is not supported on {target_os}");
}
interp_ok(())
}
/// Helper function used inside the shims of foreign functions to assert that the target OS
/// is part of the UNIX family. It panics showing a message with the `name` of the foreign function
/// if this is not the case.
@ -991,6 +1002,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
check_arg_count(args)
}
/// Check shim for variadic function.
/// Returns a tuple that consisting of an array of fixed args, and a slice of varargs.
fn check_shim_variadic<'a, const N: usize>(
&mut self,
abi: &FnAbi<'tcx, Ty<'tcx>>,
exp_abi: Conv,
link_name: Symbol,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
where
&'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
{
self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
check_vargarg_fixed_arg_count(link_name, abi, args)
}
/// Mark a machine allocation that was just created as immutable.
fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
let this = self.eval_context_mut();
@ -1184,8 +1211,10 @@ where
throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N)
}
/// Check that the number of args is at least the minumim what we expect.
pub fn check_min_arg_count<'a, 'tcx, const N: usize>(
/// Check that the number of varargs is at least the minimum what we expect.
/// Fixed args should not be included.
/// Use `check_vararg_fixed_arg_count` to extract the varargs slice from full function arguments.
pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
name: &'a str,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
@ -1193,7 +1222,35 @@ pub fn check_min_arg_count<'a, 'tcx, const N: usize>(
return interp_ok(ops);
}
throw_ub_format!(
"incorrect number of arguments for `{name}`: got {}, expected at least {}",
"not enough variadic arguments for `{name}`: got {}, expected at least {}",
args.len(),
N
)
}
/// Check the number of fixed args of a vararg function.
/// Returns a tuple that consisting of an array of fixed args, and a slice of varargs.
fn check_vargarg_fixed_arg_count<'a, 'tcx, const N: usize>(
link_name: Symbol,
abi: &FnAbi<'tcx, Ty<'tcx>>,
args: &'a [OpTy<'tcx>],
) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> {
if !abi.c_variadic {
throw_ub_format!("calling a variadic function with a non-variadic caller-side signature");
}
if abi.fixed_count != u32::try_from(N).unwrap() {
throw_ub_format!(
"incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
link_name.as_str(),
abi.fixed_count
)
}
if let Some(args) = args.split_first_chunk() {
return interp_ok(args);
}
throw_ub_format!(
"incorrect number of arguments for `{}`: got {}, expected at least {}",
link_name.as_str(),
args.len(),
N
)

View file

@ -141,7 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// FIXME: should we check for validity here? It's tricky because we do not have a
// place. Codegen does not seem to set any attributes like `noundef` for intrinsic
// calls, so we don't *have* to do anything.
let branch: bool = this.machine.rng.get_mut().gen();
let branch: bool = this.machine.rng.get_mut().random();
this.write_scalar(Scalar::from_bool(branch), dest)?;
}
@ -289,7 +289,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let a = this.read_scalar(a)?.to_f32()?;
let b = this.read_scalar(b)?.to_f32()?;
let c = this.read_scalar(c)?.to_f32()?;
let fuse: bool = this.machine.rng.get_mut().gen();
let fuse: bool = this.machine.rng.get_mut().random();
let res = if fuse {
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
@ -304,7 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let a = this.read_scalar(a)?.to_f64()?;
let b = this.read_scalar(b)?.to_f64()?;
let c = this.read_scalar(c)?.to_f64()?;
let fuse: bool = this.machine.rng.get_mut().gen();
let fuse: bool = this.machine.rng.get_mut().random();
let res = if fuse {
// FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()

View file

@ -304,7 +304,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let c = this.read_scalar(&this.project_index(&c, i)?)?;
let dest = this.project_index(&dest, i)?;
let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().gen();
let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().random();
// Works for f32 and f64.
// FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.
@ -639,8 +639,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let (right, right_len) = this.project_to_simd(right)?;
let (dest, dest_len) = this.project_to_simd(dest)?;
let index =
generic_args[2].expect_const().to_value().valtree.unwrap_branch();
let index = generic_args[2].expect_const().to_value().valtree.unwrap_branch();
let index_len = index.len();
assert_eq!(left_len, right_len);

View file

@ -15,6 +15,8 @@
#![feature(unqualified_local_imports)]
#![feature(derive_coerce_pointee)]
#![feature(arbitrary_self_types)]
#![feature(unsigned_is_multiple_of)]
#![feature(extract_if)]
// Configure clippy and other lints
#![allow(
clippy::collapsible_else_if,
@ -36,6 +38,7 @@
clippy::needless_question_mark,
clippy::needless_lifetimes,
clippy::too_long_first_doc_paragraph,
// We don't use translatable diagnostics
rustc::diagnostic_outside_of_impl,
// We are not implementing queries here so it's fine
rustc::potential_query_instability,

View file

@ -1112,10 +1112,13 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
// Call the lang item.
let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
ecx.call_function(panic, ExternAbi::Rust, &[], None, StackPopCleanup::Goto {
ret: None,
unwind: mir::UnwindAction::Unreachable,
})?;
ecx.call_function(
panic,
ExternAbi::Rust,
&[],
None,
StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
)?;
interp_ok(())
}
@ -1501,7 +1504,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
catch_unwind: None,
timing,
is_user_relevant: ecx.machine.is_user_relevant(&frame),
salt: ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_ANON_GLOBAL,
salt: ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL),
data_race: ecx.machine.data_race.as_ref().map(|_| data_race::FrameState::default()),
};
@ -1716,7 +1719,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
if unique {
CTFE_ALLOC_SALT
} else {
ecx.machine.rng.borrow_mut().gen::<usize>() % ADDRS_PER_ANON_GLOBAL
ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
}
}

View file

@ -1,9 +1,12 @@
use rand::Rng as _;
use rand::distributions::Distribution as _;
use rustc_apfloat::Float as _;
use rustc_apfloat::ieee::IeeeFloat;
/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
/// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
///
/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`.
/// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error.
/// (Subtracting 1 compensates for the integer bit.)
pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
ecx: &mut crate::MiriInterpCx<'_>,
val: F,
@ -11,12 +14,15 @@ pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
) -> F {
let rng = ecx.machine.rng.get_mut();
// Generate a random integer in the range [0, 2^PREC).
let dist = rand::distributions::Uniform::new(0, 1 << F::PRECISION);
let err = F::from_u128(dist.sample(rng))
.value
.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
// (When read as binary, the position of the first `1` determines the exponent,
// and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa,
// so this all works out.)
let r = F::from_u128(rng.random_range(0..(1 << F::PRECISION))).value;
// Multiply this with 2^(scale - PREC). The result is between 0 and
// 2^PREC * 2^(scale - PREC) = 2^scale.
let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
// give it a random sign
let err = if rng.gen::<bool>() { -err } else { err };
let err = if rng.random() { -err } else { err };
// multiple the value with (1+err)
(val * (F::from_u128(1).value + err).value).value
}

View file

@ -108,7 +108,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Pick one of the NaNs.
let nan = nans.choose(&mut *rand).unwrap();
// Non-deterministically flip the sign.
if rand.gen() {
if rand.random() {
// This will properly flip even for NaN.
-nan
} else {
@ -120,6 +120,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_ref();
// Return one side non-deterministically.
let mut rand = this.machine.rng.borrow_mut();
if rand.gen() { a } else { b }
if rand.random() { a } else { b }
}
}

View file

@ -81,7 +81,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> {
let this = self.eval_context_mut();
let align = this.malloc_align(size);
let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
let ptr =
this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
interp_ok(ptr.into())
}
@ -92,7 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
size: &OpTy<'tcx>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let memptr = this.deref_pointer(memptr)?;
let memptr = this.deref_pointer_as(memptr, this.machine.layouts.mut_raw_ptr)?;
let align = this.read_target_usize(align)?;
let size = this.read_target_usize(size)?;
@ -105,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::C.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
this.write_pointer(ptr, &memptr)?;
interp_ok(Scalar::from_i32(0))
@ -138,7 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(new_size),
new_align,
MiriMemoryKind::C.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
interp_ok(new_ptr.into())
}
@ -179,7 +180,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::C.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
interp_ok(ptr.into())
}

View file

@ -4,7 +4,6 @@ use rustc_middle::ty::{self, Instance, Ty};
use rustc_span::{BytePos, Loc, Symbol, hygiene};
use rustc_target::callconv::{Conv, FnAbi};
use crate::helpers::check_min_arg_count;
use crate::*;
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@ -34,13 +33,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
abi: &FnAbi<'tcx, Ty<'tcx>>,
link_name: Symbol,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let tcx = this.tcx;
let ptr_ty = this.machine.layouts.mut_raw_ptr.ty;
let ptr_layout = this.layout_of(ptr_ty)?;
let [flags, buf] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let [flags] = check_min_arg_count("miri_get_backtrace", args)?;
let flags = this.read_scalar(flags)?.to_u64()?;
let buf_place = this.deref_pointer_as(buf, ptr_layout)?;
let mut data = Vec::new();
for frame in this.active_thread_stack().iter().rev() {
@ -63,44 +64,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
})
.collect();
let len: u64 = ptrs.len().try_into().unwrap();
let ptr_ty = this.machine.layouts.mut_raw_ptr.ty;
let array_layout = this.layout_of(Ty::new_array(tcx.tcx, ptr_ty, len)).unwrap();
match flags {
// storage for pointers is allocated by miri
// deallocating the slice is undefined behavior with a custom global allocator
0 => {
let [_flags] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let alloc = this.allocate(array_layout, MiriMemoryKind::Rust.into())?;
// Write pointers into array
for (i, ptr) in ptrs.into_iter().enumerate() {
let place = this.project_index(&alloc, i as u64)?;
this.write_pointer(ptr, &place)?;
}
this.write_immediate(Immediate::new_slice(alloc.ptr(), len, this), dest)?;
throw_unsup_format!("miri_get_backtrace: v0 is not supported any more");
}
// storage for pointers is allocated by the caller
1 => {
let [_flags, buf] = this.check_shim(abi, Conv::Rust, link_name, args)?;
let buf_place = this.deref_pointer(buf)?;
let ptr_layout = this.layout_of(ptr_ty)?;
1 =>
for (i, ptr) in ptrs.into_iter().enumerate() {
let offset = ptr_layout.size.checked_mul(i.try_into().unwrap(), this).unwrap();
let op_place = buf_place.offset(offset, ptr_layout, this)?;
this.write_pointer(ptr, &op_place)?;
}
}
},
_ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags),
};

View file

@ -60,10 +60,10 @@ impl<'tcx> MiriMachine<'tcx> {
match ecx.tcx.sess.target.os.as_ref() {
"linux" => {
Self::null_ptr_extern_statics(ecx, &[
"__cxa_thread_atexit_impl",
"__clock_gettime64",
])?;
Self::null_ptr_extern_statics(
ecx,
&["__cxa_thread_atexit_impl", "__clock_gettime64"],
)?;
Self::weak_symbol_extern_statics(ecx, &["getrandom", "statx"])?;
}
"freebsd" => {

View file

@ -1,6 +1,6 @@
use std::any::Any;
use std::collections::BTreeMap;
use std::io::{IsTerminal, Read, SeekFrom, Write};
use std::io::{IsTerminal, SeekFrom, Write};
use std::marker::CoercePointee;
use std::ops::Deref;
use std::rc::{Rc, Weak};
@ -140,8 +140,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
_communicate_allowed: bool,
_ptr: Pointer,
_len: usize,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
_finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot read from {}", self.name());
}
@ -154,8 +154,8 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
_communicate_allowed: bool,
_ptr: Pointer,
_len: usize,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
_finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot write to {}", self.name());
}
@ -207,19 +207,16 @@ impl FileDescription for io::Stdin {
communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
let mut bytes = vec![0; len];
if !communicate_allowed {
// We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
helpers::isolation_abort_error("`read` from stdin")?;
}
let result = Read::read(&mut &*self, &mut bytes);
match result {
Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
let result = ecx.read_from_host(&*self, len, ptr)?;
finish.call(ecx, result)
}
fn is_tty(&self, communicate_allowed: bool) -> bool {
@ -237,22 +234,19 @@ impl FileDescription for io::Stdout {
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
// We allow writing to stderr even with isolation enabled.
let result = Write::write(&mut &*self, bytes);
// We allow writing to stdout even with isolation enabled.
let result = ecx.write_to_host(&*self, len, ptr)?;
// 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
// the host -- there is no good in adding extra buffering
// here.
io::stdout().flush().unwrap();
match result {
Ok(write_size) => ecx.return_write_success(write_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
finish.call(ecx, result)
}
fn is_tty(&self, communicate_allowed: bool) -> bool {
@ -270,17 +264,13 @@ impl FileDescription for io::Stderr {
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
// We allow writing to stderr even with isolation enabled.
let result = ecx.write_to_host(&*self, len, ptr)?;
// No need to flush, stderr is not buffered.
let result = Write::write(&mut &*self, bytes);
match result {
Ok(write_size) => ecx.return_write_success(write_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
finish.call(ecx, result)
}
fn is_tty(&self, communicate_allowed: bool) -> bool {
@ -302,11 +292,11 @@ impl FileDescription for NullOutput {
_communicate_allowed: bool,
_ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// We just don't write anything, but report to the user that we did.
ecx.return_write_success(len, dest)
finish.call(ecx, Ok(len))
}
}
@ -405,40 +395,41 @@ impl FdTable {
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Helper to implement `FileDescription::read`:
/// This is only used when `read` is successful.
/// `actual_read_size` should be the return value of some underlying `read` call that used
/// `bytes` as its output buffer.
/// The length of `bytes` must not exceed either the host's or the target's `isize`.
/// `bytes` is written to `buf` and the size is written to `dest`.
fn return_read_success(
/// Read data from a host `Read` type, store the result into machine memory,
/// and return whether that worked.
fn read_from_host(
&mut self,
buf: Pointer,
bytes: &[u8],
actual_read_size: usize,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
mut file: impl io::Read,
len: usize,
ptr: Pointer,
) -> InterpResult<'tcx, Result<usize, IoError>> {
let this = self.eval_context_mut();
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
// that much into the output buffer!
this.write_bytes_ptr(buf, bytes[..actual_read_size].iter().copied())?;
// The actual read size is always less than what got originally requested so this cannot fail.
this.write_int(u64::try_from(actual_read_size).unwrap(), dest)?;
interp_ok(())
let mut bytes = vec![0; len];
let result = file.read(&mut bytes);
match result {
Ok(read_size) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
// that much into the output buffer!
this.write_bytes_ptr(ptr, bytes[..read_size].iter().copied())?;
interp_ok(Ok(read_size))
}
Err(e) => interp_ok(Err(IoError::HostError(e))),
}
}
/// Helper to implement `FileDescription::write`:
/// This function is only used when `write` is successful, and writes `actual_write_size` to `dest`
fn return_write_success(
/// Write data to a host `Write` type, withthe bytes taken from machine memory.
fn write_to_host(
&mut self,
actual_write_size: usize,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
mut file: impl io::Write,
len: usize,
ptr: Pointer,
) -> InterpResult<'tcx, Result<usize, IoError>> {
let this = self.eval_context_mut();
// The actual write size is always less than what got originally requested so this cannot fail.
this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?;
interp_ok(())
let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
let result = file.write(bytes);
interp_ok(result.map_err(IoError::HostError))
}
}

View file

@ -357,7 +357,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// 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)?;
this.handle_miri_get_backtrace(abi, link_name, args)?;
}
// Resolves a Miri backtrace frame. See the README for details.
"miri_resolve_frame" => {
@ -509,7 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
memory_kind.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
ecx.write_pointer(ptr, dest)
@ -538,7 +538,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::Rust.into(),
AllocInit::Zero
AllocInit::Zero,
)?;
this.write_pointer(ptr, dest)
});
@ -599,7 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(new_size),
align,
MiriMemoryKind::Rust.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
this.write_pointer(new_ptr, dest)
});
@ -861,7 +861,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"lgammaf_r" => {
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
let x = this.read_scalar(x)?.to_f32()?;
let signp = this.deref_pointer(signp)?;
let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
// Using host floats (but it's fine, these operations do not have guaranteed precision).
let (res, sign) = x.to_host().ln_gamma();
@ -872,7 +872,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
"lgamma_r" => {
let [x, signp] = this.check_shim(abi, Conv::C, link_name, args)?;
let x = this.read_scalar(x)?.to_f64()?;
let signp = this.deref_pointer(signp)?;
let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
// Using host floats (but it's fine, these operations do not have guaranteed precision).
let (res, sign) = x.to_host().ln_gamma();

View file

@ -247,10 +247,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Call the lang item associated with this message.
let fn_item = this.tcx.require_lang_item(msg.panic_function(), None);
let instance = ty::Instance::mono(this.tcx.tcx, fn_item);
this.call_function(instance, ExternAbi::Rust, &[], None, StackPopCleanup::Goto {
ret: None,
unwind,
})?;
this.call_function(
instance,
ExternAbi::Rust,
&[],
None,
StackPopCleanup::Goto { ret: None, unwind },
)?;
}
}
interp_ok(())

View file

@ -132,16 +132,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.assert_target_os_is_unix("localtime_r");
this.check_no_isolation("`localtime_r`")?;
let timep = this.deref_pointer(timep)?;
let time_layout = this.libc_ty_layout("time_t");
let timep = this.deref_pointer_as(timep, time_layout)?;
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 sec_since_epoch: i64 =
this.read_scalar(&timep)?.to_int(time_layout.size)?.try_into().unwrap();
let dt_utc: DateTime<Utc> =
DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp");
@ -254,7 +252,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
})?;
this.write_scalar(Scalar::from_i64(qpc), &this.deref_pointer(lpPerformanceCount_op)?)?;
this.write_scalar(
Scalar::from_i64(qpc),
&this.deref_pointer_as(lpPerformanceCount_op, this.machine.layouts.i64)?,
)?;
interp_ok(Scalar::from_i32(-1)) // return non-zero on success
}

View file

@ -3,7 +3,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use crate::helpers::check_min_arg_count;
use crate::helpers::check_min_vararg_count;
use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult};
use crate::*;
@ -16,18 +16,15 @@ pub fn prctl<'tcx>(
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// We do not use `check_shim` here because `prctl` is variadic. The argument
// count is checked bellow.
ecx.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let ([op], varargs) = ecx.check_shim_variadic(abi, Conv::C, link_name, args)?;
// FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
let pr_set_name = 15;
let pr_get_name = 16;
let [op] = check_min_arg_count("prctl", args)?;
let res = match ecx.read_scalar(op)?.to_i32()? {
op if op == pr_set_name => {
let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
let [name] = check_min_vararg_count("prctl(PR_SET_NAME, ...)", varargs)?;
let name = ecx.read_scalar(name)?;
let thread = ecx.pthread_self()?;
// The Linux kernel silently truncates long names.
@ -38,7 +35,7 @@ pub fn prctl<'tcx>(
Scalar::from_u32(0)
}
op if op == pr_get_name => {
let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
let [name] = check_min_vararg_count("prctl(PR_GET_NAME, ...)", varargs)?;
let name = ecx.read_scalar(name)?;
let thread = ecx.pthread_self()?;
let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, ecx);

View file

@ -6,7 +6,7 @@ use std::io::ErrorKind;
use rustc_abi::Size;
use crate::helpers::check_min_arg_count;
use crate::helpers::check_min_vararg_count;
use crate::shims::files::FileDescription;
use crate::shims::unix::linux_like::epoll::EpollReadyEvents;
use crate::shims::unix::*;
@ -30,8 +30,8 @@ pub trait UnixFileDescription: FileDescription {
_offset: u64,
_ptr: Pointer,
_len: usize,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
_finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot pread from {}", self.name());
}
@ -46,8 +46,8 @@ pub trait UnixFileDescription: FileDescription {
_ptr: Pointer,
_len: usize,
_offset: u64,
_dest: &MPlaceTy<'tcx>,
_ecx: &mut MiriInterpCx<'tcx>,
_finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
throw_unsup_format!("cannot pwrite to {}", self.name());
}
@ -127,11 +127,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
}
fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
fn fcntl(
&mut self,
fd_num: &OpTy<'tcx>,
cmd: &OpTy<'tcx>,
varargs: &[OpTy<'tcx>],
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let [fd_num, cmd] = check_min_arg_count("fcntl", args)?;
let fd_num = this.read_scalar(fd_num)?.to_i32()?;
let cmd = this.read_scalar(cmd)?.to_i32()?;
@ -163,7 +166,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"fcntl(fd, F_DUPFD_CLOEXEC, ...)"
};
let [_, _, start] = check_min_arg_count(cmd_name, args)?;
let [start] = check_min_vararg_count(cmd_name, varargs)?;
let start = this.read_scalar(start)?.to_i32()?;
if let Some(fd) = this.machine.fds.get(fd_num) {
@ -233,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let count = usize::try_from(count).unwrap(); // now it fits in a `usize`
let communicate = this.machine.communicate();
// We temporarily dup the FD to be able to retain mutable access to `this`.
// Get the FD.
let Some(fd) = this.machine.fds.get(fd_num) else {
trace!("read: FD not found");
return this.set_last_error_and_return(LibcError("EBADF"), dest);
@ -244,13 +247,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// 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 finish = {
let dest = dest.clone();
callback!(
@capture<'tcx> {
count: usize,
dest: MPlaceTy<'tcx>,
}
|this, result: Result<usize, IoError>| {
match result {
Ok(read_size) => {
assert!(read_size <= count);
// This must fit since `count` fits.
this.write_int(u64::try_from(read_size).unwrap(), &dest)
}
Err(e) => {
this.set_last_error_and_return(e, &dest)
}
}}
)
};
match offset {
None => fd.read(communicate, buf, count, dest, this)?,
None => fd.read(communicate, buf, count, this, finish)?,
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
fd.as_unix().pread(communicate, offset, buf, count, dest, this)?
fd.as_unix().pread(communicate, offset, buf, count, this, finish)?
}
};
interp_ok(())
@ -284,13 +307,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return this.set_last_error_and_return(LibcError("EBADF"), dest);
};
let finish = {
let dest = dest.clone();
callback!(
@capture<'tcx> {
count: usize,
dest: MPlaceTy<'tcx>,
}
|this, result: Result<usize, IoError>| {
match result {
Ok(write_size) => {
assert!(write_size <= count);
// This must fit since `count` fits.
this.write_int(u64::try_from(write_size).unwrap(), &dest)
}
Err(e) => {
this.set_last_error_and_return(e, &dest)
}
}}
)
};
match offset {
None => fd.write(communicate, buf, count, dest, this)?,
None => fd.write(communicate, buf, count, this, finish)?,
Some(offset) => {
let Ok(offset) = u64::try_from(offset) else {
return this.set_last_error_and_return(LibcError("EINVAL"), dest);
};
fd.as_unix().pwrite(communicate, buf, count, offset, dest, this)?
fd.as_unix().pwrite(communicate, buf, count, offset, this, finish)?
}
};
interp_ok(())

View file

@ -205,10 +205,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"fcntl" => {
// `fcntl` is variadic. The argument count is checked based on the first argument
// in `this.fcntl()`, so we do not use `check_shim` here.
this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let result = this.fcntl(args)?;
let ([fd_num, cmd], varargs) =
this.check_shim_variadic(abi, Conv::C, link_name, args)?;
let result = this.fcntl(fd_num, cmd, varargs)?;
this.write_scalar(result, dest)?;
}
"dup" => {
@ -236,8 +235,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"open" | "open64" => {
// `open` is variadic, the third argument is only present when the second argument
// has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let result = this.open(args)?;
let ([path_raw, flag], varargs) =
this.check_shim_variadic(abi, Conv::C, link_name, args)?;
let result = this.open(path_raw, flag, varargs)?;
this.write_scalar(result, dest)?;
}
"unlink" => {
@ -354,10 +354,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"pipe2" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "solaris" | "illumos")
{
throw_unsup_format!("`pipe2` is not supported on {}", this.tcx.sess.target.os);
}
this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?;
let [pipefd, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.pipe2(pipefd, Some(flags))?;
this.write_scalar(result, dest)?;
@ -402,12 +399,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"reallocarray" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
throw_unsup_format!(
"`reallocarray` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
let [ptr, nmemb, size] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let nmemb = this.read_target_usize(nmemb)?;
@ -656,13 +648,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"sched_getaffinity" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
throw_unsup_format!(
"`sched_getaffinity` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let pid = this.read_scalar(pid)?.to_u32()?;
let cpusetsize = this.read_target_usize(cpusetsize)?;
@ -699,13 +685,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"sched_setaffinity" => {
// Currently this function does not exist on all Unixes, e.g. on macOS.
if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
throw_unsup_format!(
"`sched_setaffinity` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(&["linux", "freebsd", "android"], link_name)?;
let [pid, cpusetsize, mask] = this.check_shim(abi, Conv::C, link_name, args)?;
let pid = this.read_scalar(pid)?.to_u32()?;
let cpusetsize = this.read_target_usize(cpusetsize)?;
@ -761,16 +741,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"getentropy" => {
// This function is non-standard but exists with the same signature and behavior on
// Linux, macOS, FreeBSD and Solaris/Illumos.
if !matches!(
&*this.tcx.sess.target.os,
"linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android"
) {
throw_unsup_format!(
"`getentropy` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(
&["linux", "macos", "freebsd", "illumos", "solaris", "android"],
link_name,
)?;
let [buf, bufsize] = this.check_shim(abi, Conv::C, link_name, args)?;
let buf = this.read_pointer(buf)?;
let bufsize = this.read_target_usize(bufsize)?;
@ -797,15 +771,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"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" | "android"
) {
throw_unsup_format!(
"`getrandom` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(
&["linux", "freebsd", "illumos", "solaris", "android"],
link_name,
)?;
let [ptr, len, flags] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
@ -817,12 +786,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"arc4random_buf" => {
// This function is non-standard but exists with the same signature and
// same behavior (eg never fails) on FreeBSD and Solaris/Illumos.
if !matches!(&*this.tcx.sess.target.os, "freebsd" | "illumos" | "solaris") {
throw_unsup_format!(
"`arc4random_buf` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(&["freebsd", "illumos", "solaris"], link_name)?;
let [ptr, len] = this.check_shim(abi, Conv::C, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_target_usize(len)?;
@ -842,15 +806,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// For arm32 they did something custom, but similar enough that the same
// `_Unwind_RaiseException` impl in miri should work:
// https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
if !matches!(
&*this.tcx.sess.target.os,
"linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos"
) {
throw_unsup_format!(
"`_Unwind_RaiseException` is not supported on {}",
this.tcx.sess.target.os
);
}
this.check_target_os(
&["linux", "freebsd", "illumos", "solaris", "android", "macos"],
link_name,
)?;
// This function looks and behaves excatly like miri_start_unwind.
let [payload] = this.check_shim(abi, Conv::C, link_name, args)?;
this.handle_miri_start_unwind(payload)?;
@ -866,8 +825,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// These shims are enabled only when the caller is in the standard library.
"pthread_attr_getguardsize" if this.frame_in_std() => {
let [_attr, guard_size] = this.check_shim(abi, Conv::C, link_name, args)?;
let guard_size = this.deref_pointer(guard_size)?;
let guard_size_layout = this.libc_ty_layout("size_t");
let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?;
this.write_scalar(
Scalar::from_uint(this.machine.page_size, guard_size_layout.size),
&guard_size,
@ -893,8 +852,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.check_shim(abi, Conv::C, link_name, args)?;
let _attr_place =
this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?;
let addr_place = this.deref_pointer(addr_place)?;
let size_place = this.deref_pointer(size_place)?;
let addr_place = this.deref_pointer_as(addr_place, this.machine.layouts.usize)?;
let size_place = this.deref_pointer_as(size_place, this.machine.layouts.usize)?;
this.write_scalar(
Scalar::from_uint(this.machine.stack_addr, this.pointer_size()),
@ -928,7 +887,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let pwd = this.deref_pointer_as(pwd, this.libc_ty_layout("passwd"))?;
let buf = this.read_pointer(buf)?;
let buflen = this.read_target_usize(buflen)?;
let result = this.deref_pointer(result)?;
let result = this.deref_pointer_as(result, this.machine.layouts.mut_raw_ptr)?;
// Must be for "us".
if uid != UID {

View file

@ -60,17 +60,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// since freebsd 12 the former form can be expected.
"stat" | "stat@FBSD_1.0" => {
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_stat(path, buf)?;
let result = this.macos_fbsd_solarish_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat@FBSD_1.0" => {
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat@FBSD_1.0" => {
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir_r" | "readdir_r@FBSD_1.0" => {

View file

@ -13,7 +13,7 @@ use rustc_abi::Size;
use rustc_data_structures::fx::FxHashMap;
use self::shims::time::system_time_to_duration;
use crate::helpers::check_min_arg_count;
use crate::helpers::check_min_vararg_count;
use crate::shims::files::{EvalContextExt as _, FileDescription, FileDescriptionRef};
use crate::shims::os_str::bytes_to_os_str;
use crate::shims::unix::fd::{FlockOp, UnixFileDescription};
@ -35,16 +35,13 @@ impl FileDescription for FileHandle {
communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
let mut bytes = vec![0; len];
let result = (&mut &self.file).read(&mut bytes);
match result {
Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
let result = ecx.read_from_host(&self.file, len, ptr)?;
finish.call(ecx, result)
}
fn write<'tcx>(
@ -52,16 +49,13 @@ impl FileDescription for FileHandle {
communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
let result = (&mut &self.file).write(bytes);
match result {
Ok(write_size) => ecx.return_write_success(write_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
let result = ecx.write_to_host(&self.file, len, ptr)?;
finish.call(ecx, result)
}
fn seek<'tcx>(
@ -119,8 +113,8 @@ impl UnixFileDescription for FileHandle {
offset: u64,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
let mut bytes = vec![0; len];
@ -137,11 +131,17 @@ impl UnixFileDescription for FileHandle {
.expect("failed to restore file position, this shouldn't be possible");
res
};
let result = f();
match result {
Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
let result = match f() {
Ok(read_size) => {
// If reading to `bytes` did not fail, we write those bytes to the buffer.
// Crucially, if fewer than `bytes.len()` bytes were read, only write
// that much into the output buffer!
ecx.write_bytes_ptr(ptr, bytes[..read_size].iter().copied())?;
Ok(read_size)
}
Err(e) => Err(IoError::HostError(e)),
};
finish.call(ecx, result)
}
fn pwrite<'tcx>(
@ -150,8 +150,8 @@ impl UnixFileDescription for FileHandle {
ptr: Pointer,
len: usize,
offset: u64,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
assert!(communicate_allowed, "isolation should have prevented even opening a file");
// Emulates pwrite using seek + write + seek to restore cursor position.
@ -169,10 +169,7 @@ impl UnixFileDescription for FileHandle {
res
};
let result = f();
match result {
Ok(write_size) => ecx.return_write_success(write_size, dest),
Err(e) => ecx.set_last_error_and_return(e, dest),
}
finish.call(ecx, result.map_err(IoError::HostError))
}
fn flock<'tcx>(
@ -273,7 +270,7 @@ impl UnixFileDescription for FileHandle {
impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn macos_fbsd_solaris_write_buf(
fn macos_fbsd_solarish_write_stat_buf(
&mut self,
metadata: FileMetadata,
buf_op: &OpTy<'tcx>,
@ -321,9 +318,9 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
if matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
// FIXME: write st_fstype field once libc is updated.
// https://github.com/rust-lang/libc/pull/4145
//this.write_int_fields_named(&[("st_fstype", 0)], &buf)?;
let st_fstype = this.project_field_named(&buf, "st_fstype")?;
// This is an array; write 0 into first element so that it encodes the empty string.
this.write_int(0, &this.project_index(&st_fstype, 0)?)?;
}
interp_ok(0)
@ -452,9 +449,12 @@ fn maybe_sync_file(
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
let [path_raw, flag] = check_min_arg_count("open", args)?;
fn open(
&mut self,
path_raw: &OpTy<'tcx>,
flag: &OpTy<'tcx>,
varargs: &[OpTy<'tcx>],
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let path_raw = this.read_pointer(path_raw)?;
@ -507,7 +507,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Get the mode. On macOS, the argument type `mode_t` is actually `u16`, but
// C integer promotion rules mean that on the ABI level, it gets passed as `u32`
// (see https://github.com/rust-lang/rust/issues/71915).
let [_, _, mode] = check_min_arg_count("open(pathname, O_CREAT, ...)", args)?;
let [mode] = check_min_vararg_count("open(pathname, O_CREAT, ...)", varargs)?;
let mode = this.read_scalar(mode)?.to_u32()?;
#[cfg(unix)]
@ -668,7 +668,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
}
fn macos_fbsd_solaris_stat(
fn macos_fbsd_solarish_stat(
&mut self,
path_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
@ -694,11 +694,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Err(err) => return this.set_last_error_and_return_i32(err),
};
interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?))
interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))
}
// `lstat` is used to get symlink metadata.
fn macos_fbsd_solaris_lstat(
fn macos_fbsd_solarish_lstat(
&mut self,
path_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
@ -726,10 +726,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Err(err) => return this.set_last_error_and_return_i32(err),
};
interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?))
interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))
}
fn macos_fbsd_solaris_fstat(
fn macos_fbsd_solarish_fstat(
&mut self,
fd_op: &OpTy<'tcx>,
buf_op: &OpTy<'tcx>,
@ -756,7 +756,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(metadata) => metadata,
Err(err) => return this.set_last_error_and_return_i32(err),
};
interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?))
interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))
}
fn linux_statx(
@ -1109,7 +1109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
dirent_layout.align.abi,
MiriMemoryKind::Runtime.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
let entry: Pointer = entry.into();
@ -1169,6 +1169,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
let dirp = this.read_target_usize(dirp_op)?;
let result_place = this.deref_pointer_as(result_op, this.machine.layouts.mut_raw_ptr)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
@ -1254,15 +1255,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
_ => unreachable!(),
}
let result_place = this.deref_pointer(result_op)?;
this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;
Scalar::from_i32(0)
}
None => {
// end of stream: return 0, assign *result=NULL
this.write_null(&this.deref_pointer(result_op)?)?;
this.write_null(&result_place)?;
Scalar::from_i32(0)
}
Some(Err(e)) => {
@ -1548,7 +1547,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
fn mkstemp(&mut self, template_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
use rand::seq::SliceRandom;
use rand::seq::IndexedRandom;
// POSIX defines the template string.
const TEMPFILE_TEMPLATE_STR: &str = "XXXXXX";

View file

@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(new_size),
align,
MiriMemoryKind::Mmap.into(),
AllocInit::Zero
AllocInit::Zero,
)?;
interp_ok(Scalar::from_pointer(ptr, this))

View file

@ -51,20 +51,20 @@ impl FileDescription for EventFd {
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// We're treating the buffer as a `u64`.
let ty = ecx.machine.layouts.u64;
// Check the size of slice, and return error only if the size of the slice < 8.
if len < ty.size.bytes_usize() {
return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
return finish.call(ecx, Err(ErrorKind::InvalidInput.into()));
}
// Turn the pointer into a place at the right type.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
eventfd_read(buf_place, dest, self, ecx)
eventfd_read(buf_place, self, ecx, finish)
}
/// A write call adds the 8-byte integer value supplied in
@ -84,20 +84,20 @@ impl FileDescription for EventFd {
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// We're treating the buffer as a `u64`.
let ty = ecx.machine.layouts.u64;
// Check the size of slice, and return error only if the size of the slice < 8.
if len < ty.layout.size.bytes_usize() {
return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
return finish.call(ecx, Err(ErrorKind::InvalidInput.into()));
}
// Turn the pointer into a place at the right type.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
eventfd_write(buf_place, dest, self, ecx)
eventfd_write(buf_place, self, ecx, finish)
}
fn as_unix(&self) -> &dyn UnixFileDescription {
@ -183,15 +183,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// else just add the user-supplied value to current counter.
fn eventfd_write<'tcx>(
buf_place: MPlaceTy<'tcx>,
dest: &MPlaceTy<'tcx>,
eventfd: FileDescriptionRef<EventFd>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// Figure out which value we should add.
let num = ecx.read_scalar(&buf_place)?.to_u64()?;
// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
if num == u64::MAX {
return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
return finish.call(ecx, Err(ErrorKind::InvalidInput.into()));
}
match eventfd.counter.get().checked_add(num) {
@ -219,16 +219,14 @@ fn eventfd_write<'tcx>(
ecx.check_and_update_readiness(eventfd)?;
// Return how many bytes we consumed from the user-provided buffer.
return ecx.write_int(buf_place.layout.size.bytes(), dest);
return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize()));
}
None | Some(u64::MAX) => {
// We can't update the state, so we have to block.
if eventfd.is_nonblock {
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
return finish.call(ecx, Err(ErrorKind::WouldBlock.into()));
}
let dest = dest.clone();
eventfd.blocked_write_tid.borrow_mut().push(ecx.active_thread());
let weak_eventfd = FileDescriptionRef::downgrade(&eventfd);
@ -239,7 +237,7 @@ fn eventfd_write<'tcx>(
@capture<'tcx> {
num: u64,
buf_place: MPlaceTy<'tcx>,
dest: MPlaceTy<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
weak_eventfd: WeakFileDescriptionRef<EventFd>,
}
|this, unblock: UnblockKind| {
@ -247,7 +245,7 @@ fn eventfd_write<'tcx>(
// When we get unblocked, try again. We know the ref is still valid,
// otherwise there couldn't be a `write` that unblocks us.
let eventfd_ref = weak_eventfd.upgrade().unwrap();
eventfd_write(buf_place, &dest, eventfd_ref, this)
eventfd_write(buf_place, eventfd_ref, this, finish)
}
),
);
@ -260,9 +258,9 @@ fn eventfd_write<'tcx>(
/// else just return the current counter value to the caller and set the counter to 0.
fn eventfd_read<'tcx>(
buf_place: MPlaceTy<'tcx>,
dest: &MPlaceTy<'tcx>,
eventfd: FileDescriptionRef<EventFd>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// Set counter to 0, get old value.
let counter = eventfd.counter.replace(0);
@ -270,9 +268,8 @@ fn eventfd_read<'tcx>(
// Block when counter == 0.
if counter == 0 {
if eventfd.is_nonblock {
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
return finish.call(ecx, Err(ErrorKind::WouldBlock.into()));
}
let dest = dest.clone();
eventfd.blocked_read_tid.borrow_mut().push(ecx.active_thread());
@ -283,7 +280,7 @@ fn eventfd_read<'tcx>(
callback!(
@capture<'tcx> {
buf_place: MPlaceTy<'tcx>,
dest: MPlaceTy<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
weak_eventfd: WeakFileDescriptionRef<EventFd>,
}
|this, unblock: UnblockKind| {
@ -291,7 +288,7 @@ fn eventfd_read<'tcx>(
// When we get unblocked, try again. We know the ref is still valid,
// otherwise there couldn't be a `write` that unblocks us.
let eventfd_ref = weak_eventfd.upgrade().unwrap();
eventfd_read(buf_place, &dest, eventfd_ref, this)
eventfd_read(buf_place, eventfd_ref, this, finish)
}
),
);
@ -317,7 +314,7 @@ fn eventfd_read<'tcx>(
ecx.check_and_update_readiness(eventfd)?;
// Tell userspace how many bytes we put into the buffer.
return ecx.write_int(buf_place.layout.size.bytes(), dest);
return finish.call(ecx, Ok(buf_place.layout.size.bytes_usize()));
}
interp_ok(())
}

View file

@ -1,5 +1,5 @@
use crate::concurrency::sync::FutexRef;
use crate::helpers::check_min_arg_count;
use crate::helpers::check_min_vararg_count;
use crate::*;
struct LinuxFutex {
@ -10,7 +10,7 @@ struct LinuxFutex {
/// `args` is the arguments *including* the syscall number.
pub fn futex<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
args: &[OpTy<'tcx>],
varargs: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// The amount of arguments used depends on the type of futex operation.
@ -21,7 +21,7 @@ pub fn futex<'tcx>(
// may or may not be left out from the `syscall()` call.
// Therefore we don't use `check_arg_count` here, but only check for the
// number of arguments to fall within a range.
let [_, addr, op, val] = check_min_arg_count("`syscall(SYS_futex, ...)`", args)?;
let [addr, op, val] = check_min_vararg_count("`syscall(SYS_futex, ...)`", varargs)?;
// The first three arguments (after the syscall number itself) are the same to all futex operations:
// (int *addr, int op, int val).
@ -55,14 +55,16 @@ pub fn futex<'tcx>(
let wait_bitset = op & !futex_realtime == futex_wait_bitset;
let (timeout, bitset) = if wait_bitset {
let [_, _, _, _, timeout, uaddr2, bitset] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", args)?;
let [_, _, _, timeout, uaddr2, bitset] = check_min_vararg_count(
"`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`",
varargs,
)?;
let _timeout = ecx.read_pointer(timeout)?;
let _uaddr2 = ecx.read_pointer(uaddr2)?;
(timeout, ecx.read_scalar(bitset)?.to_u32()?)
} else {
let [_, _, _, _, timeout] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", args)?;
let [_, _, _, timeout] =
check_min_vararg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", varargs)?;
(timeout, u32::MAX)
};
@ -156,14 +158,24 @@ pub fn futex<'tcx>(
.futex
.clone();
let dest = dest.clone();
ecx.futex_wait(
futex_ref,
bitset,
timeout,
Scalar::from_target_isize(0, ecx), // retval_succ
Scalar::from_target_isize(-1, ecx), // retval_timeout
dest.clone(),
LibcError("ETIMEDOUT"), // errno_timeout
callback!(
@capture<'tcx> {
dest: MPlaceTy<'tcx>,
}
|ecx, unblock: UnblockKind| match unblock {
UnblockKind::Ready => {
ecx.write_int(0, &dest)
}
UnblockKind::TimedOut => {
ecx.set_last_error_and_return(LibcError("ETIMEDOUT"), &dest)
}
}
),
);
} else {
// The futex value doesn't match the expected value, so we return failure
@ -190,8 +202,10 @@ pub fn futex<'tcx>(
let futex_ref = futex_ref.futex.clone();
let bitset = if op == futex_wake_bitset {
let [_, _, _, _, timeout, uaddr2, bitset] =
check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?;
let [_, _, _, timeout, uaddr2, bitset] = check_min_vararg_count(
"`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`",
varargs,
)?;
let _timeout = ecx.read_pointer(timeout)?;
let _uaddr2 = ecx.read_pointer(uaddr2)?;
ecx.read_scalar(bitset)?.to_u32()?
@ -205,16 +219,8 @@ pub fn futex<'tcx>(
// will see the latest value on addr which could be changed by our caller
// before doing the syscall.
ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
let mut n = 0;
#[expect(clippy::arithmetic_side_effects)]
for _ in 0..val {
if ecx.futex_wake(&futex_ref, bitset)? {
n += 1;
} else {
break;
}
}
ecx.write_scalar(Scalar::from_target_isize(n, ecx), dest)?;
let woken = ecx.futex_wake(&futex_ref, bitset, val.try_into().unwrap())?;
ecx.write_scalar(Scalar::from_target_isize(woken.try_into().unwrap(), ecx), dest)?;
}
op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op),
}

View file

@ -2,7 +2,7 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use crate::helpers::check_min_arg_count;
use crate::helpers::check_min_vararg_count;
use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
use crate::shims::unix::linux_like::sync::futex;
use crate::*;
@ -14,9 +14,7 @@ pub fn syscall<'tcx>(
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
// We do not use `check_shim` here because `syscall` is variadic. The argument
// count is checked bellow.
ecx.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let ([op], varargs) = ecx.check_shim_variadic(abi, Conv::C, link_name, args)?;
// The syscall variadic function is legal to call with more arguments than needed,
// extra arguments are simply ignored. The important check is that when we use an
// argument, we have to also check all arguments *before* it to ensure that they
@ -26,14 +24,13 @@ pub fn syscall<'tcx>(
let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?;
let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
let [op] = check_min_arg_count("syscall", args)?;
match ecx.read_target_usize(op)? {
// `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<K, V>).
num if num == sys_getrandom => {
// Used by getrandom 0.1
// The first argument is the syscall id, so skip over it.
let [_, ptr, len, flags] = check_min_arg_count("syscall(SYS_getrandom, ...)", args)?;
let [ptr, len, flags] = check_min_vararg_count("syscall(SYS_getrandom, ...)", varargs)?;
let ptr = ecx.read_pointer(ptr)?;
let len = ecx.read_target_usize(len)?;
@ -47,10 +44,10 @@ pub fn syscall<'tcx>(
}
// `futex` is used by some synchronization primitives.
num if num == sys_futex => {
futex(ecx, args, dest)?;
futex(ecx, varargs, dest)?;
}
num if num == sys_eventfd2 => {
let [_, initval, flags] = check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?;
let [initval, flags] = check_min_vararg_count("syscall(SYS_evetfd2, ...)", varargs)?;
let result = ecx.eventfd(initval, flags)?;
ecx.write_int(result.to_i32()?, dest)?;

View file

@ -2,13 +2,20 @@ use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::{Conv, FnAbi};
use super::sync::EvalContextExt as _;
use crate::helpers::check_min_arg_count;
use super::sync::{EvalContextExt as _, MacOsFutexTimeout};
use crate::shims::unix::*;
use crate::*;
pub fn is_dyn_sym(_name: &str) -> bool {
false
pub fn is_dyn_sym(name: &str) -> bool {
match name {
// These only became available with macOS 11.0, so std looks them up dynamically.
"os_sync_wait_on_address"
| "os_sync_wait_on_address_with_deadline"
| "os_sync_wait_on_address_with_timeout"
| "os_sync_wake_by_address_any"
| "os_sync_wake_by_address_all" => true,
_ => false,
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@ -40,17 +47,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
"stat" | "stat64" | "stat$INODE64" => {
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_stat(path, buf)?;
let result = this.macos_fbsd_solarish_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" | "lstat$INODE64" => {
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" | "fstat$INODE64" => {
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"opendir$INODE64" => {
@ -69,10 +76,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(result, dest)?;
}
"ioctl" => {
// `ioctl` is variadic. The argument count is checked based on the first argument
// in `this.ioctl()`, so we do not use `check_shim` here.
this.check_abi_and_shim_symbol_clash(abi, Conv::C, link_name)?;
let result = this.ioctl(args)?;
let ([fd_num, cmd], varargs) =
this.check_shim_variadic(abi, Conv::C, link_name, args)?;
let result = this.ioctl(fd_num, cmd, varargs)?;
this.write_scalar(result, dest)?;
}
@ -216,6 +222,58 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(res, dest)?;
}
// Futex primitives
"os_sync_wait_on_address" => {
let [addr_op, value_op, size_op, flags_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
this.os_sync_wait_on_address(
addr_op,
value_op,
size_op,
flags_op,
MacOsFutexTimeout::None,
dest,
)?;
}
"os_sync_wait_on_address_with_deadline" => {
let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
this.os_sync_wait_on_address(
addr_op,
value_op,
size_op,
flags_op,
MacOsFutexTimeout::Absolute { clock_op, timeout_op },
dest,
)?;
}
"os_sync_wait_on_address_with_timeout" => {
let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
this.os_sync_wait_on_address(
addr_op,
value_op,
size_op,
flags_op,
MacOsFutexTimeout::Relative { clock_op, timeout_op },
dest,
)?;
}
"os_sync_wake_by_address_any" => {
let [addr_op, size_op, flags_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
this.os_sync_wake_by_address(
addr_op, size_op, flags_op, /* all */ false, dest,
)?;
}
"os_sync_wake_by_address_all" => {
let [addr_op, size_op, flags_op] =
this.check_shim(abi, Conv::C, link_name, args)?;
this.os_sync_wake_by_address(
addr_op, size_op, flags_op, /* all */ true, dest,
)?;
}
"os_unfair_lock_lock" => {
let [lock_op] = this.check_shim(abi, Conv::C, link_name, args)?;
this.os_unfair_lock_lock(lock_op)?;
@ -243,12 +301,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(EmulateItemResult::NeedsReturn)
}
fn ioctl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
fn ioctl(
&mut self,
fd_num: &OpTy<'tcx>,
cmd: &OpTy<'tcx>,
_varargs: &[OpTy<'tcx>],
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let fioclex = this.eval_libc_u64("FIOCLEX");
let [fd_num, cmd] = check_min_arg_count("ioctl", args)?;
let fd_num = this.read_scalar(fd_num)?.to_i32()?;
let cmd = this.read_scalar(cmd)?.to_u64()?;

View file

@ -10,8 +10,12 @@
//! and we do not detect copying of the lock, but macOS doesn't guarantee anything
//! in that case either.
use std::cell::Cell;
use std::time::Duration;
use rustc_abi::Size;
use crate::concurrency::sync::FutexRef;
use crate::*;
#[derive(Clone)]
@ -20,6 +24,26 @@ enum MacOsUnfairLock {
Active { mutex_ref: MutexRef },
}
pub enum MacOsFutexTimeout<'a, 'tcx> {
None,
Relative { clock_op: &'a OpTy<'tcx>, timeout_op: &'a OpTy<'tcx> },
Absolute { clock_op: &'a OpTy<'tcx>, timeout_op: &'a OpTy<'tcx> },
}
/// Metadata for a macOS futex.
///
/// Since macOS 11.0, Apple has exposed the previously private futex API consisting
/// of `os_sync_wait_on_address` (and friends) and `os_sync_wake_by_address_{any, all}`.
/// These work with different value sizes and flags, which are validated to be consistent.
/// This structure keeps track of both the futex queue and these values.
struct MacOsFutex {
futex: FutexRef,
/// The size in bytes of the atomic primitive underlying this futex.
size: Cell<u64>,
/// Whether the futex is shared across process boundaries.
shared: Cell<bool>,
}
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_get_data<'a>(
@ -30,7 +54,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
'tcx: 'a,
{
let this = self.eval_context_mut();
let lock = this.deref_pointer(lock_ptr)?;
let lock = this.deref_pointer_as(lock_ptr, this.libc_ty_layout("os_unfair_lock_s"))?;
this.lazy_sync_get_data(
&lock,
Size::ZERO, // offset for init tracking
@ -54,6 +78,198 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Implements [`os_sync_wait_on_address`], [`os_sync_wait_on_address_with_deadline`]
/// and [`os_sync_wait_on_address_with_timeout`].
///
/// [`os_sync_wait_on_address`]: https://developer.apple.com/documentation/os/os_sync_wait_on_address?language=objc
/// [`os_sync_wait_on_address_with_deadline`]: https://developer.apple.com/documentation/os/os_sync_wait_on_address_with_deadline?language=objc
/// [`os_sync_wait_on_address_with_timeout`]: https://developer.apple.com/documentation/os/os_sync_wait_on_address_with_timeout?language=objc
fn os_sync_wait_on_address(
&mut self,
addr_op: &OpTy<'tcx>,
value_op: &OpTy<'tcx>,
size_op: &OpTy<'tcx>,
flags_op: &OpTy<'tcx>,
timeout: MacOsFutexTimeout<'_, 'tcx>,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let none = this.eval_libc_u32("OS_SYNC_WAIT_ON_ADDRESS_NONE");
let shared = this.eval_libc_u32("OS_SYNC_WAIT_ON_ADDRESS_SHARED");
let absolute_clock = this.eval_libc_u32("OS_CLOCK_MACH_ABSOLUTE_TIME");
let ptr = this.read_pointer(addr_op)?;
let value = this.read_scalar(value_op)?.to_u64()?;
let size = this.read_target_usize(size_op)?;
let flags = this.read_scalar(flags_op)?.to_u32()?;
let clock_timeout = match timeout {
MacOsFutexTimeout::None => None,
MacOsFutexTimeout::Relative { clock_op, timeout_op } => {
let clock = this.read_scalar(clock_op)?.to_u32()?;
let timeout = this.read_scalar(timeout_op)?.to_u64()?;
Some((clock, TimeoutAnchor::Relative, timeout))
}
MacOsFutexTimeout::Absolute { clock_op, timeout_op } => {
let clock = this.read_scalar(clock_op)?.to_u32()?;
let timeout = this.read_scalar(timeout_op)?.to_u64()?;
Some((clock, TimeoutAnchor::Absolute, timeout))
}
};
// Perform validation of the arguments.
let addr = ptr.addr().bytes();
if addr == 0
|| !matches!(size, 4 | 8)
|| !addr.is_multiple_of(size)
|| (flags != none && flags != shared)
|| clock_timeout
.is_some_and(|(clock, _, timeout)| clock != absolute_clock || timeout == 0)
{
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
return interp_ok(());
}
let is_shared = flags == shared;
let timeout = clock_timeout.map(|(_, anchor, timeout)| {
// The only clock that is currenlty supported is the monotonic clock.
// While the deadline argument of `os_sync_wait_on_address_with_deadline`
// is actually not in nanoseconds but in the units of `mach_current_time`,
// the two are equivalent in miri.
(TimeoutClock::Monotonic, anchor, Duration::from_nanos(timeout))
});
// See the Linux futex implementation for why this fence exists.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
let layout = this.machine.layouts.uint(Size::from_bytes(size)).unwrap();
let futex_val = this
.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Acquire)?
.to_bits(Size::from_bytes(size))?;
let futex = this
.get_sync_or_init(ptr, |_| {
MacOsFutex {
futex: Default::default(),
size: Cell::new(size),
shared: Cell::new(is_shared),
}
})
.unwrap();
// Detect mismatches between the flags and sizes used on this address
// by comparing it with the parameters used by the other waiters in
// the current list. If the list is currently empty, update those
// parameters.
if futex.futex.waiters() == 0 {
futex.size.set(size);
futex.shared.set(is_shared);
} else if futex.size.get() != size || futex.shared.get() != is_shared {
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
return interp_ok(());
}
if futex_val == value.into() {
// If the values are the same, we have to block.
let futex_ref = futex.futex.clone();
let dest = dest.clone();
this.futex_wait(
futex_ref.clone(),
u32::MAX, // bitset
timeout,
callback!(
@capture<'tcx> {
dest: MPlaceTy<'tcx>,
futex_ref: FutexRef,
}
|this, unblock: UnblockKind| {
match unblock {
UnblockKind::Ready => {
let remaining = futex_ref.waiters().try_into().unwrap();
this.write_scalar(Scalar::from_i32(remaining), &dest)
}
UnblockKind::TimedOut => {
this.set_last_error_and_return(LibcError("ETIMEDOUT"), &dest)
}
}
}
),
);
} else {
// else retrieve the current number of waiters.
let waiters = futex.futex.waiters().try_into().unwrap();
this.write_scalar(Scalar::from_i32(waiters), dest)?;
}
interp_ok(())
}
/// Implements [`os_sync_wake_by_address_all`] and [`os_sync_wake_by_address_any`].
///
/// [`os_sync_wake_by_address_all`]: https://developer.apple.com/documentation/os/os_sync_wake_by_address_all?language=objc
/// [`os_sync_wake_by_address_any`]: https://developer.apple.com/documentation/os/os_sync_wake_by_address_any?language=objc
fn os_sync_wake_by_address(
&mut self,
addr_op: &OpTy<'tcx>,
size_op: &OpTy<'tcx>,
flags_op: &OpTy<'tcx>,
all: bool,
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let none = this.eval_libc_u32("OS_SYNC_WAKE_BY_ADDRESS_NONE");
let shared = this.eval_libc_u32("OS_SYNC_WAKE_BY_ADDRESS_SHARED");
let ptr = this.read_pointer(addr_op)?;
let size = this.read_target_usize(size_op)?;
let flags = this.read_scalar(flags_op)?.to_u32()?;
// Perform validation of the arguments.
let addr = ptr.addr().bytes();
if addr == 0 || !matches!(size, 4 | 8) || (flags != none && flags != shared) {
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
return interp_ok(());
}
let is_shared = flags == shared;
let Some(futex) = this.get_sync_or_init(ptr, |_| {
MacOsFutex {
futex: Default::default(),
size: Cell::new(size),
shared: Cell::new(is_shared),
}
}) else {
// No AllocId, or no live allocation at that AllocId. Return an
// error code. (That seems nicer than silently doing something
// non-intuitive.) This means that if an address gets reused by a
// new allocation, we'll use an independent futex queue for this...
// that seems acceptable.
this.set_last_error_and_return(LibcError("ENOENT"), dest)?;
return interp_ok(());
};
if futex.futex.waiters() == 0 {
this.set_last_error_and_return(LibcError("ENOENT"), dest)?;
return interp_ok(());
// If there are waiters in the queue, they have all used the parameters
// stored in `futex` (we check this in `os_sync_wait_on_address` above).
// Detect mismatches between "our" parameters and the parameters used by
// the waiters and return an error in that case.
} else if futex.size.get() != size || futex.shared.get() != is_shared {
this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
return interp_ok(());
}
let futex_ref = futex.futex.clone();
// See the Linux futex implementation for why this fence exists.
this.atomic_fence(AtomicFenceOrd::SeqCst)?;
this.futex_wake(&futex_ref, u32::MAX, if all { usize::MAX } else { 1 })?;
this.write_scalar(Scalar::from_i32(0), dest)?;
interp_ok(())
}
fn os_unfair_lock_lock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();

View file

@ -116,7 +116,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
align,
MiriMemoryKind::Mmap.into(),
// mmap guarantees new mappings are zero-init.
AllocInit::Zero
AllocInit::Zero,
)?;
interp_ok(Scalar::from_pointer(ptr, this))

View file

@ -87,17 +87,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// File related shims
"stat" | "stat64" => {
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_stat(path, buf)?;
let result = this.macos_fbsd_solarish_stat(path, buf)?;
this.write_scalar(result, dest)?;
}
"lstat" | "lstat64" => {
let [path, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_lstat(path, buf)?;
let result = this.macos_fbsd_solarish_lstat(path, buf)?;
this.write_scalar(result, dest)?;
}
"fstat" | "fstat64" => {
let [fd, buf] = this.check_shim(abi, Conv::C, link_name, args)?;
let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
this.write_scalar(result, dest)?;
}
"readdir" => {
@ -163,7 +163,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
throw_unsup_format!("pset_info is only supported with list==NULL");
}
let cpus = this.deref_pointer(cpus)?;
let cpus = this.deref_pointer_as(cpus, this.machine.layouts.u32)?;
this.write_scalar(Scalar::from_u32(this.machine.num_cpus), &cpus)?;
this.write_null(dest)?;
}

View file

@ -170,7 +170,7 @@ fn mutex_create<'tcx>(
mutex_ptr: &OpTy<'tcx>,
kind: MutexKind,
) -> InterpResult<'tcx, PthreadMutex> {
let mutex = ecx.deref_pointer(mutex_ptr)?;
let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
let id = ecx.machine.sync.mutex_create();
let data = PthreadMutex { mutex_ref: id, kind };
ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data.clone())?;
@ -186,7 +186,7 @@ fn mutex_get_data<'tcx, 'a>(
where
'tcx: 'a,
{
let mutex = ecx.deref_pointer(mutex_ptr)?;
let mutex = ecx.deref_pointer_as(mutex_ptr, ecx.libc_ty_layout("pthread_mutex_t"))?;
ecx.lazy_sync_get_data(
&mutex,
mutex_init_offset(ecx)?,
@ -265,7 +265,7 @@ fn rwlock_get_data<'tcx, 'a>(
where
'tcx: 'a,
{
let rwlock = ecx.deref_pointer(rwlock_ptr)?;
let rwlock = ecx.deref_pointer_as(rwlock_ptr, ecx.libc_ty_layout("pthread_rwlock_t"))?;
ecx.lazy_sync_get_data(
&rwlock,
rwlock_init_offset(ecx)?,
@ -383,7 +383,7 @@ fn cond_create<'tcx>(
cond_ptr: &OpTy<'tcx>,
clock: ClockId,
) -> InterpResult<'tcx, PthreadCondvar> {
let cond = ecx.deref_pointer(cond_ptr)?;
let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
let id = ecx.machine.sync.condvar_create();
let data = PthreadCondvar { id, clock };
ecx.lazy_sync_init(&cond, cond_init_offset(ecx)?, data)?;
@ -397,7 +397,7 @@ fn cond_get_data<'tcx, 'a>(
where
'tcx: 'a,
{
let cond = ecx.deref_pointer(cond_ptr)?;
let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?;
ecx.lazy_sync_get_data(
&cond,
cond_init_offset(ecx)?,
@ -760,7 +760,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let clock_id = condattr_get_clock_id(this, attr_op)?;
this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?;
this.write_scalar(
Scalar::from_i32(clock_id),
&this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?,
)?;
interp_ok(())
}

View file

@ -5,9 +5,7 @@
use std::cell::{Cell, OnceCell, RefCell};
use std::collections::VecDeque;
use std::io;
use std::io::{ErrorKind, Read};
use rustc_abi::Size;
use std::io::ErrorKind;
use crate::concurrency::VClock;
use crate::shims::files::{
@ -92,10 +90,10 @@ impl FileDescription for AnonSocket {
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
anonsocket_read(self, len, ptr, dest, ecx)
anonsocket_read(self, ptr, len, ecx, finish)
}
fn write<'tcx>(
@ -103,10 +101,10 @@ impl FileDescription for AnonSocket {
_communicate_allowed: bool,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
anonsocket_write(self, ptr, len, dest, ecx)
anonsocket_write(self, ptr, len, ecx, finish)
}
fn as_unix(&self) -> &dyn UnixFileDescription {
@ -119,25 +117,25 @@ fn anonsocket_write<'tcx>(
self_ref: FileDescriptionRef<AnonSocket>,
ptr: Pointer,
len: usize,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// Always succeed on write size 0.
// ("If count is zero and fd refers to a file other than a regular file, the results are not specified.")
if len == 0 {
return ecx.return_write_success(0, dest);
return finish.call(ecx, Ok(0));
}
// We are writing to our peer's readbuf.
let Some(peer_fd) = self_ref.peer_fd().upgrade() else {
// If the upgrade from Weak to Rc fails, it indicates that all read ends have been
// closed. It is an error to write even if there would be space.
return ecx.set_last_error_and_return(ErrorKind::BrokenPipe, dest);
return finish.call(ecx, Err(ErrorKind::BrokenPipe.into()));
};
let Some(writebuf) = &peer_fd.readbuf else {
// Writing to the read end of a pipe.
return ecx.set_last_error_and_return(IoError::LibcError("EBADF"), dest);
return finish.call(ecx, Err(IoError::LibcError("EBADF")));
};
// Let's see if we can write.
@ -145,13 +143,12 @@ fn anonsocket_write<'tcx>(
if available_space == 0 {
if self_ref.is_nonblock {
// Non-blocking socketpair with a full buffer.
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
return finish.call(ecx, Err(ErrorKind::WouldBlock.into()));
} else {
self_ref.blocked_write_tid.borrow_mut().push(ecx.active_thread());
// Blocking socketpair with a full buffer.
// Block the current thread; only keep a weak ref for this.
let weak_self_ref = FileDescriptionRef::downgrade(&self_ref);
let dest = dest.clone();
ecx.block_thread(
BlockReason::UnnamedSocket,
None,
@ -160,14 +157,14 @@ fn anonsocket_write<'tcx>(
weak_self_ref: WeakFileDescriptionRef<AnonSocket>,
ptr: Pointer,
len: usize,
dest: MPlaceTy<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
}
|this, unblock: UnblockKind| {
assert_eq!(unblock, UnblockKind::Ready);
// If we got unblocked, then our peer successfully upgraded its weak
// ref to us. That means we can also upgrade our weak ref.
let self_ref = weak_self_ref.upgrade().unwrap();
anonsocket_write(self_ref, ptr, len, &dest, this)
anonsocket_write(self_ref, ptr, len, this, finish)
}
),
);
@ -180,9 +177,9 @@ fn anonsocket_write<'tcx>(
writebuf.clock.join(clock);
});
// Do full write / partial write based on the space available.
let actual_write_size = len.min(available_space);
let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
writebuf.buf.extend(&bytes[..actual_write_size]);
let write_size = len.min(available_space);
let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap();
assert_eq!(actual_write_size, write_size);
// Need to stop accessing peer_fd so that it can be notified.
drop(writebuf);
@ -197,7 +194,7 @@ fn anonsocket_write<'tcx>(
// The kernel does this even if the fd was already readable before, so we follow suit.
ecx.check_and_update_readiness(peer_fd)?;
return ecx.return_write_success(actual_write_size, dest);
return finish.call(ecx, Ok(write_size));
}
interp_ok(())
}
@ -205,14 +202,14 @@ fn anonsocket_write<'tcx>(
/// Read from AnonSocket and return the number of bytes read.
fn anonsocket_read<'tcx>(
self_ref: FileDescriptionRef<AnonSocket>,
len: usize,
ptr: Pointer,
dest: &MPlaceTy<'tcx>,
len: usize,
ecx: &mut MiriInterpCx<'tcx>,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
) -> InterpResult<'tcx> {
// Always succeed on read size 0.
if len == 0 {
return ecx.return_read_success(ptr, &[], 0, dest);
return finish.call(ecx, Ok(0));
}
let Some(readbuf) = &self_ref.readbuf else {
@ -225,43 +222,41 @@ fn anonsocket_read<'tcx>(
if self_ref.peer_fd().upgrade().is_none() {
// Socketpair with no peer and empty buffer.
// 0 bytes successfully read indicates end-of-file.
return ecx.return_read_success(ptr, &[], 0, dest);
return finish.call(ecx, Ok(0));
} else if self_ref.is_nonblock {
// Non-blocking socketpair with writer and empty buffer.
// https://linux.die.net/man/2/read
// EAGAIN or EWOULDBLOCK can be returned for socket,
// POSIX.1-2001 allows either error to be returned for this case.
// Since there is no ErrorKind for EAGAIN, WouldBlock is used.
return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
return finish.call(ecx, Err(ErrorKind::WouldBlock.into()));
} else {
self_ref.blocked_read_tid.borrow_mut().push(ecx.active_thread());
// Blocking socketpair with writer and empty buffer.
// Block the current thread; only keep a weak ref for this.
let weak_self_ref = FileDescriptionRef::downgrade(&self_ref);
let dest = dest.clone();
ecx.block_thread(
BlockReason::UnnamedSocket,
None,
callback!(
@capture<'tcx> {
weak_self_ref: WeakFileDescriptionRef<AnonSocket>,
len: usize,
ptr: Pointer,
dest: MPlaceTy<'tcx>,
len: usize,
finish: DynMachineCallback<'tcx, Result<usize, IoError>>,
}
|this, unblock: UnblockKind| {
assert_eq!(unblock, UnblockKind::Ready);
// If we got unblocked, then our peer successfully upgraded its weak
// ref to us. That means we can also upgrade our weak ref.
let self_ref = weak_self_ref.upgrade().unwrap();
anonsocket_read(self_ref, len, ptr, &dest, this)
anonsocket_read(self_ref, ptr, len, this, finish)
}
),
);
}
} else {
// There's data to be read!
let mut bytes = vec![0; len];
let mut readbuf = readbuf.borrow_mut();
// Synchronize with all previous writes to this buffer.
// FIXME: this over-synchronizes; a more precise approach would be to
@ -270,7 +265,7 @@ fn anonsocket_read<'tcx>(
// Do full read / partial read based on the space available.
// Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap();
let read_size = ecx.read_from_host(&mut readbuf.buf, len, ptr)?.unwrap();
// Need to drop before others can access the readbuf again.
drop(readbuf);
@ -293,7 +288,7 @@ fn anonsocket_read<'tcx>(
ecx.check_and_update_readiness(peer_fd)?;
};
return ecx.return_read_success(ptr, &bytes, actual_read_size, dest);
return finish.call(ecx, Ok(read_size));
}
interp_ok(())
}
@ -362,7 +357,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let domain = this.read_scalar(domain)?.to_i32()?;
let mut flags = this.read_scalar(type_)?.to_i32()?;
let protocol = this.read_scalar(protocol)?.to_i32()?;
let sv = this.deref_pointer(sv)?;
// This is really a pointer to `[i32; 2]` but we use a ptr-to-first-element representation.
let sv = this.deref_pointer_as(sv, this.machine.layouts.i32)?;
let mut is_sock_nonblock = false;

View file

@ -218,7 +218,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let token = this.read_target_isize(token)?;
let buf = this.read_pointer(buf)?;
let size = this.deref_pointer(size)?;
let size = this.deref_pointer_as(size, this.machine.layouts.u32)?;
if token != -4 {
throw_unsup_format!(

View file

@ -266,7 +266,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::WinHeap.into(),
init
init,
)?;
this.write_pointer(ptr, dest)?;
}
@ -299,7 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Size::from_bytes(size),
Align::from_bytes(align).unwrap(),
MiriMemoryKind::WinHeap.into(),
AllocInit::Uninit
AllocInit::Uninit,
)?;
this.write_pointer(new_ptr, dest)?;
}
@ -335,7 +335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Initialize with `0`.
this.write_bytes_ptr(
system_info.ptr(),
iter::repeat(0u8).take(system_info.layout.size.bytes_usize()),
iter::repeat_n(0u8, system_info.layout.size.bytes_usize()),
)?;
// Set selected fields.
this.write_int_fields_named(
@ -523,7 +523,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
let handle = this.read_scalar(handle)?;
let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name
let thread = match Handle::try_from_scalar(handle, this)? {
Ok(Handle::Thread(thread)) => Ok(thread),
@ -725,7 +725,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"GetConsoleMode" if this.frame_in_std() => {
let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?;
this.read_target_isize(console)?;
this.deref_pointer(mode)?;
this.deref_pointer_as(mode, this.machine.layouts.u32)?;
// Indicate an error.
this.write_null(dest)?;
}

View file

@ -29,7 +29,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
{
let this = self.eval_context_mut();
let init_once = this.deref_pointer(init_once_ptr)?;
let init_once =
this.deref_pointer_as(init_once_ptr, this.windows_ty_layout("INIT_ONCE"))?;
let init_offset = Size::ZERO;
this.lazy_sync_get_data(
@ -85,7 +86,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let id = this.init_once_get_data(init_once_op)?.id;
let flags = this.read_scalar(flags_op)?.to_u32()?;
let pending_place = this.deref_pointer(pending_op)?;
// PBOOL is int*
let pending_place = this.deref_pointer_as(pending_op, this.machine.layouts.i32)?;
let context = this.read_pointer(context_op)?;
if flags != 0 {
@ -210,14 +212,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
.futex
.clone();
let dest = dest.clone();
this.futex_wait(
futex_ref,
u32::MAX, // bitset
timeout,
Scalar::from_i32(1), // retval_succ
Scalar::from_i32(0), // retval_timeout
dest.clone(),
IoError::WindowsError("ERROR_TIMEOUT"), // errno_timeout
callback!(
@capture<'tcx> {
dest: MPlaceTy<'tcx>
}
|this, unblock: UnblockKind| {
match unblock {
UnblockKind::Ready => {
this.write_int(1, &dest)
}
UnblockKind::TimedOut => {
this.set_last_error(IoError::WindowsError("ERROR_TIMEOUT"))?;
this.write_int(0, &dest)
}
}
}
),
);
}
@ -242,7 +257,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let futex_ref = futex_ref.futex.clone();
this.futex_wake(&futex_ref, u32::MAX)?;
this.futex_wake(&futex_ref, u32::MAX, 1)?;
interp_ok(())
}
@ -262,7 +277,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
let futex_ref = futex_ref.futex.clone();
while this.futex_wake(&futex_ref, u32::MAX)? {}
this.futex_wake(&futex_ref, u32::MAX, usize::MAX)?;
interp_ok(())
}

View file

@ -29,7 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let thread = if this.ptr_is_null(this.read_pointer(thread_op)?)? {
None
} else {
let thread_info_place = this.deref_pointer(thread_op)?;
let thread_info_place = this.deref_pointer_as(thread_op, this.machine.layouts.u32)?;
Some(thread_info_place)
};

View file

@ -105,6 +105,18 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"windows-targets",
]
[[package]]
name = "gimli"
version = "0.29.0"
@ -178,6 +190,7 @@ dependencies = [
"cfg-if",
"getrandom 0.1.16",
"getrandom 0.2.15",
"getrandom 0.3.1",
"libc",
"num_cpus",
"page_size",
@ -359,6 +372,15 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
@ -507,3 +529,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]

View file

@ -8,13 +8,14 @@ version = "0.1.0"
edition = "2021"
[dependencies]
# all dependencies (and their transitive ones) listed here can be used in `tests/`.
# all dependencies (and their transitive ones) listed here can be used in `tests/*-dep`.
libc = "0.2"
num_cpus = "1.10.1"
cfg-if = "1"
getrandom_01 = { package = "getrandom", version = "0.1" }
getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] }
getrandom_03 = { package = "getrandom", version = "0.3" }
[target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
tempfile = "3"

View file

@ -8,5 +8,5 @@ fn main() {
fn test_file_open_missing_needed_mode() {
let name = b"missing_arg.txt\0";
let name_ptr = name.as_ptr().cast::<libc::c_char>();
let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3
let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) }; //~ ERROR: Undefined Behavior: not enough variadic arguments
}

View file

@ -1,8 +1,8 @@
error: Undefined Behavior: incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3
error: Undefined Behavior: not enough variadic arguments for `open(pathname, O_CREAT, ...)`: got 0, expected at least 1
--> tests/fail-dep/libc/fs/unix_open_missing_required_mode.rs:LL:CC
|
LL | ... { libc::open(name_ptr, libc::O_CREAT) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of arguments for `open(pathname, O_CREAT, ...)`: got 2, expected at least 3
LL | let _fd = unsafe { libc::open(name_ptr, libc::O_CREAT) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not enough variadic arguments for `open(pathname, O_CREAT, ...)`: got 0, expected at least 1
|
= 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

View file

@ -1,10 +1,13 @@
extern "Rust" {
fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>;
fn miri_backtrace_size(flags: u64) -> usize;
fn miri_get_backtrace(flags: u64, buf: *mut *mut ());
fn miri_resolve_frame(ptr: *mut (), flags: u64);
}
fn main() {
let frames = unsafe { miri_get_backtrace(0) };
let size = unsafe { miri_backtrace_size(0) };
let mut frames = vec![std::ptr::null_mut(); size];
unsafe { miri_get_backtrace(1, frames.as_mut_ptr()) };
for frame in frames.iter() {
unsafe {
miri_resolve_frame(*frame, 0); //~ ERROR: Undefined Behavior: bad declaration of miri_resolve_frame - should return a struct with 5 fields

View file

@ -0,0 +1,17 @@
//@ignore-target: windows # File handling is not implemented yet
//@compile-flags: -Zmiri-disable-isolation
use std::ffi::{CString, OsStr, c_char, c_int};
use std::os::unix::ffi::OsStrExt;
// Declare a variadic function as non-variadic.
extern "C" {
fn open(path: *const c_char, oflag: c_int) -> c_int;
}
fn main() {
let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed");
let _fd = unsafe {
open(c_path.as_ptr(), /* value does not matter */ 0)
//~^ ERROR: calling a variadic function with a non-variadic caller-side signature
};
}

View file

@ -0,0 +1,15 @@
error: Undefined Behavior: calling a variadic function with a non-variadic caller-side signature
--> tests/fail/shims/non_vararg_signature_mismatch.rs:LL:CC
|
LL | open(c_path.as_ptr(), /* value does not matter */ 0)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a variadic function with a non-variadic caller-side signature
|
= 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 tests/fail/shims/non_vararg_signature_mismatch.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

View file

@ -0,0 +1,16 @@
//@ignore-target: windows # File handling is not implemented yet
//@compile-flags: -Zmiri-disable-isolation
use std::ffi::{CString, OsStr, c_char, c_int};
use std::os::unix::ffi::OsStrExt;
extern "C" {
fn open(path: *const c_char, ...) -> c_int;
}
fn main() {
let c_path = CString::new(OsStr::new("./text").as_bytes()).expect("CString::new failed");
let _fd = unsafe {
open(c_path.as_ptr(), /* value does not matter */ 0)
//~^ ERROR: incorrect number of fixed arguments for variadic function
};
}

View file

@ -0,0 +1,15 @@
error: Undefined Behavior: incorrect number of fixed arguments for variadic function `open`: got 1, expected 2
--> tests/fail/shims/wrong_fixed_arg_count.rs:LL:CC
|
LL | open(c_path.as_ptr(), /* value does not matter */ 0)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of fixed arguments for variadic function `open`: got 1, expected 2
|
= 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 tests/fail/shims/wrong_fixed_arg_count.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

View file

@ -0,0 +1,276 @@
//@only-target: darwin
//@compile-flags: -Zmiri-preemption-rate=0
use std::time::{Duration, Instant};
use std::{io, ptr, thread};
fn wake_nobody() {
let futex = 0;
// Wake 1 waiter. Expect ENOENT as nobody is waiting.
unsafe {
assert_eq!(
libc::os_sync_wake_by_address_any(
ptr::from_ref(&futex).cast_mut().cast(),
size_of::<i32>(),
libc::OS_SYNC_WAKE_BY_ADDRESS_NONE
),
-1
);
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOENT);
}
}
fn wake_dangling() {
let futex = Box::new(0);
let ptr = ptr::from_ref(&futex).cast_mut().cast();
drop(futex);
// Expect error since this is now "unmapped" memory.
unsafe {
assert_eq!(
libc::os_sync_wake_by_address_any(
ptr,
size_of::<i32>(),
libc::OS_SYNC_WAKE_BY_ADDRESS_NONE
),
-1
);
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOENT);
}
}
fn wait_wrong_val() {
let futex: i32 = 123;
// Only wait if the futex value is 456.
unsafe {
assert_eq!(
libc::os_sync_wait_on_address(
ptr::from_ref(&futex).cast_mut().cast(),
456,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_NONE
),
0,
);
}
}
fn wait_timeout() {
let start = Instant::now();
let futex: i32 = 123;
// Wait for 200ms, with nobody waking us up early.
unsafe {
assert_eq!(
libc::os_sync_wait_on_address_with_timeout(
ptr::from_ref(&futex).cast_mut().cast(),
123,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_NONE,
libc::OS_CLOCK_MACH_ABSOLUTE_TIME,
200_000_000,
),
-1,
);
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
}
assert!((200..1000).contains(&start.elapsed().as_millis()));
}
fn wait_absolute_timeout() {
let start = Instant::now();
// Get the current monotonic timestamp.
#[allow(deprecated)]
let mut deadline = unsafe { libc::mach_absolute_time() };
// Add 200ms.
// What we should be doing here is call `mach_timebase_info` to determine the
// unit used for `deadline`, but we know what Miri returns for that function:
// the unit is nanoseconds.
deadline += 200_000_000;
let futex: i32 = 123;
// Wait for 200ms from now, with nobody waking us up early.
unsafe {
assert_eq!(
libc::os_sync_wait_on_address_with_deadline(
ptr::from_ref(&futex).cast_mut().cast(),
123,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_NONE,
libc::OS_CLOCK_MACH_ABSOLUTE_TIME,
deadline,
),
-1,
);
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
}
assert!((200..1000).contains(&start.elapsed().as_millis()));
}
fn wait_wake() {
let start = Instant::now();
static mut FUTEX: i32 = 0;
let t = thread::spawn(move || {
thread::sleep(Duration::from_millis(200));
unsafe {
assert_eq!(
libc::os_sync_wake_by_address_any(
(&raw const FUTEX).cast_mut().cast(),
size_of::<i32>(),
libc::OS_SYNC_WAKE_BY_ADDRESS_NONE,
),
0,
);
}
});
unsafe {
assert_eq!(
libc::os_sync_wait_on_address(
(&raw const FUTEX).cast_mut().cast(),
0,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_NONE,
),
0,
);
}
// When running this in stress-gc mode, things can take quite long.
// So the timeout is 3000 ms.
assert!((200..3000).contains(&start.elapsed().as_millis()));
t.join().unwrap();
}
fn wait_wake_multiple() {
let val = 0i32;
let futex = &val;
thread::scope(|s| {
// Spawn some threads and make them wait on the futex.
for i in 0..4 {
s.spawn(move || unsafe {
assert_eq!(
libc::os_sync_wait_on_address(
ptr::from_ref(futex).cast_mut().cast(),
0,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_NONE,
),
// The last two threads will be woken at the same time,
// but for the first two threads the remaining number
// of waiters should be strictly decreasing.
if i < 2 { 3 - i } else { 0 },
);
});
thread::yield_now();
}
// Wake the threads up again.
unsafe {
assert_eq!(
libc::os_sync_wake_by_address_any(
ptr::from_ref(futex).cast_mut().cast(),
size_of::<i32>(),
libc::OS_SYNC_WAKE_BY_ADDRESS_NONE,
),
0
);
assert_eq!(
libc::os_sync_wake_by_address_any(
ptr::from_ref(futex).cast_mut().cast(),
size_of::<i32>(),
libc::OS_SYNC_WAKE_BY_ADDRESS_NONE,
),
0
);
// Wake both remaining threads at the same time.
assert_eq!(
libc::os_sync_wake_by_address_all(
ptr::from_ref(futex).cast_mut().cast(),
size_of::<i32>(),
libc::OS_SYNC_WAKE_BY_ADDRESS_NONE,
),
0
);
}
})
}
fn param_mismatch() {
let futex = 0;
thread::scope(|s| {
s.spawn(|| {
unsafe {
assert_eq!(
libc::os_sync_wait_on_address_with_timeout(
ptr::from_ref(&futex).cast_mut().cast(),
0,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_NONE,
libc::OS_CLOCK_MACH_ABSOLUTE_TIME,
400_000_000,
),
-1,
);
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::ETIMEDOUT);
}
});
s.spawn(|| {
thread::yield_now();
unsafe {
assert_eq!(
libc::os_sync_wait_on_address(
ptr::from_ref(&futex).cast_mut().cast(),
0,
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_SHARED,
),
-1,
);
// This call fails because it uses the shared flag whereas the first waiter didn't.
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
}
});
thread::yield_now();
unsafe {
assert_eq!(
libc::os_sync_wake_by_address_any(
ptr::from_ref(&futex).cast_mut().cast(),
size_of::<i32>(),
libc::OS_SYNC_WAIT_ON_ADDRESS_SHARED,
),
-1,
);
// This call fails because it uses the shared flag whereas the waiter didn't.
assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EINVAL);
}
});
}
fn main() {
wake_nobody();
wake_dangling();
wait_wrong_val();
wait_timeout();
wait_absolute_timeout();
wait_wake();
wait_wake_multiple();
param_mismatch();
}

View file

@ -3,7 +3,7 @@
//@revisions: isolation no_isolation
//@[no_isolation]compile-flags: -Zmiri-disable-isolation
/// Test direct calls of getrandom 0.1 and 0.2.
/// Test direct calls of getrandom 0.1, 0.2 and 0.3.
fn main() {
let mut data = vec![0; 16];
@ -13,4 +13,6 @@ fn main() {
getrandom_01::getrandom(&mut data).unwrap();
getrandom_02::getrandom(&mut data).unwrap();
getrandom_03::fill(&mut data).unwrap();
}

View file

@ -230,10 +230,10 @@ fn test_two_same_fd_in_same_epoll_instance() {
//Two notification should be received.
let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
let expected_value = 5 as u64;
check_epoll_wait::<8>(epfd, &[
(expected_event, expected_value),
(expected_event, expected_value),
]);
check_epoll_wait::<8>(
epfd,
&[(expected_event, expected_value), (expected_event, expected_value)],
);
}
fn test_epoll_eventfd() {
@ -290,10 +290,10 @@ fn test_epoll_socketpair_both_sides() {
let expected_value0 = fds[0] as u64;
let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value1 = fds[1] as u64;
check_epoll_wait::<8>(epfd, &[
(expected_event0, expected_value0),
(expected_event1, expected_value1),
]);
check_epoll_wait::<8>(
epfd,
&[(expected_event0, expected_value0), (expected_event1, expected_value1)],
);
// Read from fds[0].
let mut buf: [u8; 5] = [0; 5];
@ -453,10 +453,10 @@ fn test_socketpair_read() {
let expected_value0 = fds[0] as u64;
let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap();
let expected_value1 = fds[1] as u64;
check_epoll_wait::<8>(epfd, &[
(expected_event0, expected_value0),
(expected_event1, expected_value1),
]);
check_epoll_wait::<8>(
epfd,
&[(expected_event0, expected_value0), (expected_event1, expected_value1)],
);
// Read 3 bytes from fds[0].
let mut buf: [u8; 3] = [0; 3];

View file

@ -13,7 +13,7 @@ use ui_test::custom_flags::edition::Edition;
use ui_test::dependencies::DependencyBuilder;
use ui_test::per_test_config::TestConfig;
use ui_test::spanned::Spanned;
use ui_test::{CommandBuilder, Config, Format, Match, OutputConflictHandling, status_emitter};
use ui_test::{CommandBuilder, Config, Format, Match, ignore_output_conflict, status_emitter};
#[derive(Copy, Clone, Debug)]
enum Mode {
@ -82,9 +82,18 @@ fn build_native_lib() -> PathBuf {
native_lib_path
}
struct WithDependencies {
bless: bool,
}
/// Does *not* set any args or env vars, since it is shared between the test runner and
/// run_dep_mode.
fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> Config {
fn miri_config(
target: &str,
path: &str,
mode: Mode,
with_dependencies: Option<WithDependencies>,
) -> Config {
// Miri is rustc-like, so we create a default builder for rustc and modify it
let mut program = CommandBuilder::rustc();
program.program = miri_path();
@ -119,22 +128,26 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
// keep in sync with `./miri run`
config.comment_defaults.base().add_custom("edition", Edition("2021".into()));
if with_dependencies {
config.comment_defaults.base().set_custom("dependencies", DependencyBuilder {
program: CommandBuilder {
// Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary.
// (It's a separate crate, so we don't get an env var from cargo.)
program: miri_path()
.with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)),
// There is no `cargo miri build` so we just use `cargo miri run`.
args: ["miri", "run"].into_iter().map(Into::into).collect(),
// Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>.
envs: vec![("RUSTFLAGS".into(), None)],
..CommandBuilder::cargo()
if let Some(WithDependencies { bless }) = with_dependencies {
config.comment_defaults.base().set_custom(
"dependencies",
DependencyBuilder {
program: CommandBuilder {
// Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary.
// (It's a separate crate, so we don't get an env var from cargo.)
program: miri_path()
.with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)),
// There is no `cargo miri build` so we just use `cargo miri run`.
args: ["miri", "run"].into_iter().map(Into::into).collect(),
// Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>.
envs: vec![("RUSTFLAGS".into(), None)],
..CommandBuilder::cargo()
},
crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"),
build_std: None,
bless_lockfile: bless,
},
crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"),
build_std: None,
});
);
}
config
}
@ -146,7 +159,20 @@ fn run_tests(
with_dependencies: bool,
tmpdir: &Path,
) -> Result<()> {
// Handle command-line arguments.
let mut args = ui_test::Args::test()?;
args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
let with_dependencies = with_dependencies.then_some(WithDependencies { bless: args.bless });
let mut config = miri_config(target, path, mode, with_dependencies);
config.with_args(&args);
config.bless_command = Some("./miri test --bless".into());
if env::var_os("MIRI_SKIP_UI_CHECKS").is_some() {
assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time");
config.output_conflict_handling = ignore_output_conflict;
}
// Add a test env var to do environment communication tests.
config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into())));
@ -182,16 +208,6 @@ fn run_tests(
config.program.args.push(flag);
}
// Handle command-line arguments.
let mut args = ui_test::Args::test()?;
args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
config.with_args(&args);
config.bless_command = Some("./miri test --bless".into());
if env::var_os("MIRI_SKIP_UI_CHECKS").is_some() {
assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time");
config.output_conflict_handling = OutputConflictHandling::Ignore;
}
eprintln!(" Compiler: {}", config.program.display());
ui_test::run_tests_generic(
// Only run one test suite. In the future we can add all test suites to one `Vec` and run
@ -327,7 +343,8 @@ fn main() -> Result<()> {
}
fn run_dep_mode(target: String, args: impl Iterator<Item = OsString>) -> Result<()> {
let mut config = miri_config(&target, "", Mode::RunDep, /* with dependencies */ true);
let mut config =
miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false }));
config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice
config.fill_host_and_target()?;
config.program.args = args.collect();