diff --git a/README.md b/README.md index 0c5b591e72..504759eabd 100644 --- a/README.md +++ b/README.md @@ -199,6 +199,10 @@ up the sysroot. If you are using `miri` (the Miri driver) directly, see the Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS` environment variable: +* `-Zmiri-check-number-validity` enables checking of integer and float validity + (e.g., they must be initialized and not carry pointer provenance) as part of + enforcing validity invariants. This has no effect when + `-Zmiri-disable-validation` is present. * `-Zmiri-compare-exchange-weak-failure-rate=` changes the failure rate of `compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail). You can change it to any value between `0.0` and `1.0`, where `1.0` means it diff --git a/rust-version b/rust-version index fd8c69e112..d43b9e8197 100644 --- a/rust-version +++ b/rust-version @@ -1 +1 @@ -db062de72b0a064f45b6f86894cbdc7c0ec68844 +68ca579406f2fa9ec62710e4a4d5d3e07a168d3c diff --git a/src/bin/miri.rs b/src/bin/miri.rs index 84e66db2a7..cf32e63322 100644 --- a/src/bin/miri.rs +++ b/src/bin/miri.rs @@ -313,6 +313,9 @@ fn main() { "-Zmiri-symbolic-alignment-check" => { miri_config.check_alignment = miri::AlignmentCheck::Symbolic; } + "-Zmiri-check-number-validity" => { + miri_config.check_number_validity = true; + } "-Zmiri-disable-abi-check" => { miri_config.check_abi = false; } diff --git a/src/eval.rs b/src/eval.rs index 5d8d332fcd..ac32af30c1 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -66,6 +66,8 @@ pub struct MiriConfig { pub stacked_borrows: bool, /// Controls alignment checking. pub check_alignment: AlignmentCheck, + /// Controls integer and float validity (e.g., initialization) checking. + pub check_number_validity: bool, /// Controls function [ABI](Abi) checking. pub check_abi: bool, /// Action for an op requiring communication with the host. @@ -104,6 +106,7 @@ impl Default for MiriConfig { validate: true, stacked_borrows: true, check_alignment: AlignmentCheck::Int, + check_number_validity: false, check_abi: true, isolated_op: IsolatedOp::Reject(RejectOpWith::Abort), ignore_leaks: false, diff --git a/src/machine.rs b/src/machine.rs index 23278a4891..0ead28b36c 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -304,6 +304,9 @@ pub struct Evaluator<'mir, 'tcx> { /// Whether to enforce the validity invariant. pub(crate) validate: bool, + /// Whether to enforce validity (e.g., initialization) of integers and floats. + pub(crate) enforce_number_validity: bool, + /// Whether to enforce [ABI](Abi) of function calls. pub(crate) enforce_abi: bool, @@ -356,6 +359,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> { tls: TlsData::default(), isolated_op: config.isolated_op, validate: config.validate, + enforce_number_validity: config.check_number_validity, enforce_abi: config.check_abi, file_handler: Default::default(), dir_handler: Default::default(), @@ -426,6 +430,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> { ecx.machine.validate } + #[inline(always)] + fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { + ecx.machine.enforce_number_validity + } + #[inline(always)] fn enforce_abi(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { ecx.machine.enforce_abi diff --git a/src/shims/posix/sync.rs b/src/shims/posix/sync.rs index 782765c778..606f58a207 100644 --- a/src/shims/posix/sync.rs +++ b/src/shims/posix/sync.rs @@ -1,5 +1,8 @@ use std::time::SystemTime; +use rustc_hir::LangItem; +use rustc_middle::ty::{layout::TyAndLayout, query::TyCtxtAt, subst::Subst, Ty}; + use crate::*; use thread::Time; @@ -44,7 +47,7 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>( attr_op: &OpTy<'tcx, Tag>, kind: impl Into>, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(attr_op, 0, kind, ecx.machine.layouts.i32) + ecx.write_scalar_at_offset(attr_op, 0, kind, layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32)) } // pthread_mutex_t is between 24 and 48 bytes, depending on the platform. @@ -79,7 +82,7 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>( mutex_op, offset, kind, - ecx.machine.layouts.i32, + layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32), AtomicWriteOp::Relaxed, ) } @@ -100,7 +103,7 @@ fn mutex_set_id<'mir, 'tcx: 'mir>( mutex_op, 4, id, - ecx.machine.layouts.u32, + layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32), AtomicWriteOp::Relaxed, ) } @@ -144,7 +147,7 @@ fn rwlock_set_id<'mir, 'tcx: 'mir>( rwlock_op, 4, id, - ecx.machine.layouts.u32, + layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32), AtomicWriteOp::Relaxed, ) } @@ -211,7 +214,7 @@ fn cond_set_id<'mir, 'tcx: 'mir>( cond_op, 4, id, - ecx.machine.layouts.u32, + layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32), AtomicWriteOp::Relaxed, ) } @@ -244,7 +247,12 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>( cond_op: &OpTy<'tcx, Tag>, clock_id: impl Into>, ) -> InterpResult<'tcx, ()> { - ecx.write_scalar_at_offset(cond_op, 8, clock_id, ecx.machine.layouts.i32) + ecx.write_scalar_at_offset( + cond_op, + 8, + clock_id, + layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32), + ) } /// Try to reacquire the mutex associated with the condition variable after we @@ -788,3 +796,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx Ok(0) } } + +fn layout_of_maybe_uninit<'tcx>(tcx: TyCtxtAt<'tcx>, param: Ty<'tcx>) -> TyAndLayout<'tcx> { + let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None); + let def_ty = tcx.type_of(def_id); + let ty = def_ty.subst(*tcx, &[param.into()]); + + let param_env = tcx.param_env(def_id); + tcx.layout_of(param_env.and(ty)).unwrap() +} diff --git a/tests/compile-fail/uninit_float.rs b/tests/compile-fail/uninit_float.rs new file mode 100644 index 0000000000..06953e1ced --- /dev/null +++ b/tests/compile-fail/uninit_float.rs @@ -0,0 +1,8 @@ +// compile-flags: -Zmiri-check-number-validity + +// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. + +fn main() { + let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; + //~^ ERROR type validation failed at .value: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes +} diff --git a/tests/compile-fail/uninit_integer.rs b/tests/compile-fail/uninit_integer.rs new file mode 100644 index 0000000000..757f69c050 --- /dev/null +++ b/tests/compile-fail/uninit_integer.rs @@ -0,0 +1,8 @@ +// compile-flags: -Zmiri-check-number-validity + +// This test is from https://github.com/rust-lang/miri/issues/1340#issue-600900312. + +fn main() { + let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; + //~^ ERROR type validation failed at .value: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes +} diff --git a/tests/compile-fail/uninit_integer_signed.rs b/tests/compile-fail/uninit_integer_signed.rs new file mode 100644 index 0000000000..bb5d7314a7 --- /dev/null +++ b/tests/compile-fail/uninit_integer_signed.rs @@ -0,0 +1,8 @@ +// compile-flags: -Zmiri-check-number-validity + +// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. + +fn main() { + let _val = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; + //~^ ERROR type validation failed at .value: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes +} diff --git a/tests/compile-fail/uninit_raw_ptr.rs b/tests/compile-fail/uninit_raw_ptr.rs new file mode 100644 index 0000000000..beb9ad1270 --- /dev/null +++ b/tests/compile-fail/uninit_raw_ptr.rs @@ -0,0 +1,4 @@ +fn main() { + let _val = unsafe { std::mem::MaybeUninit::<*const u8>::uninit().assume_init() }; + //~^ ERROR type validation failed at .value: encountered uninitialized raw pointer +} diff --git a/tests/run-pass/uninit_number_ignored.rs b/tests/run-pass/uninit_number_ignored.rs new file mode 100644 index 0000000000..77d6af6e99 --- /dev/null +++ b/tests/run-pass/uninit_number_ignored.rs @@ -0,0 +1,8 @@ +// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312. +// This test passes because -Zmiri-check-number-validity is not passed. + +fn main() { + let _val1 = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; + let _val2 = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; + let _val3 = unsafe { std::mem::MaybeUninit::::uninit().assume_init() }; +}