Skip to content

Commit a8b976e

Browse files
committed
Auto merge of #1904 - camelid:uninit-num, r=RalfJung
Add flag to check for uninitialized numbers Closes #1340. Companion rustc PR that implements this in the Miri engine: rust-lang/rust#88670 r? `@RalfJung`
2 parents 3f2c9ee + 6dd1082 commit a8b976e

11 files changed

+79
-7
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ up the sysroot. If you are using `miri` (the Miri driver) directly, see the
199199
Miri adds its own set of `-Z` flags, which are usually set via the `MIRIFLAGS`
200200
environment variable:
201201

202+
* `-Zmiri-check-number-validity` enables checking of integer and float validity
203+
(e.g., they must be initialized and not carry pointer provenance) as part of
204+
enforcing validity invariants. This has no effect when
205+
`-Zmiri-disable-validation` is present.
202206
* `-Zmiri-compare-exchange-weak-failure-rate=<rate>` changes the failure rate of
203207
`compare_exchange_weak` operations. The default is `0.8` (so 4 out of 5 weak ops will fail).
204208
You can change it to any value between `0.0` and `1.0`, where `1.0` means it

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
db062de72b0a064f45b6f86894cbdc7c0ec68844
1+
68ca579406f2fa9ec62710e4a4d5d3e07a168d3c

src/bin/miri.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ fn main() {
313313
"-Zmiri-symbolic-alignment-check" => {
314314
miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
315315
}
316+
"-Zmiri-check-number-validity" => {
317+
miri_config.check_number_validity = true;
318+
}
316319
"-Zmiri-disable-abi-check" => {
317320
miri_config.check_abi = false;
318321
}

src/eval.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ pub struct MiriConfig {
6666
pub stacked_borrows: bool,
6767
/// Controls alignment checking.
6868
pub check_alignment: AlignmentCheck,
69+
/// Controls integer and float validity (e.g., initialization) checking.
70+
pub check_number_validity: bool,
6971
/// Controls function [ABI](Abi) checking.
7072
pub check_abi: bool,
7173
/// Action for an op requiring communication with the host.
@@ -104,6 +106,7 @@ impl Default for MiriConfig {
104106
validate: true,
105107
stacked_borrows: true,
106108
check_alignment: AlignmentCheck::Int,
109+
check_number_validity: false,
107110
check_abi: true,
108111
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
109112
ignore_leaks: false,

src/machine.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ pub struct Evaluator<'mir, 'tcx> {
304304
/// Whether to enforce the validity invariant.
305305
pub(crate) validate: bool,
306306

307+
/// Whether to enforce validity (e.g., initialization) of integers and floats.
308+
pub(crate) enforce_number_validity: bool,
309+
307310
/// Whether to enforce [ABI](Abi) of function calls.
308311
pub(crate) enforce_abi: bool,
309312

@@ -356,6 +359,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
356359
tls: TlsData::default(),
357360
isolated_op: config.isolated_op,
358361
validate: config.validate,
362+
enforce_number_validity: config.check_number_validity,
359363
enforce_abi: config.check_abi,
360364
file_handler: Default::default(),
361365
dir_handler: Default::default(),
@@ -426,6 +430,11 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
426430
ecx.machine.validate
427431
}
428432

433+
#[inline(always)]
434+
fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
435+
ecx.machine.enforce_number_validity
436+
}
437+
429438
#[inline(always)]
430439
fn enforce_abi(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
431440
ecx.machine.enforce_abi

src/shims/posix/sync.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use std::time::SystemTime;
22

3+
use rustc_hir::LangItem;
4+
use rustc_middle::ty::{layout::TyAndLayout, query::TyCtxtAt, subst::Subst, Ty};
5+
36
use crate::*;
47
use thread::Time;
58

@@ -44,7 +47,7 @@ fn mutexattr_set_kind<'mir, 'tcx: 'mir>(
4447
attr_op: &OpTy<'tcx, Tag>,
4548
kind: impl Into<ScalarMaybeUninit<Tag>>,
4649
) -> InterpResult<'tcx, ()> {
47-
ecx.write_scalar_at_offset(attr_op, 0, kind, ecx.machine.layouts.i32)
50+
ecx.write_scalar_at_offset(attr_op, 0, kind, layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32))
4851
}
4952

5053
// pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
@@ -79,7 +82,7 @@ fn mutex_set_kind<'mir, 'tcx: 'mir>(
7982
mutex_op,
8083
offset,
8184
kind,
82-
ecx.machine.layouts.i32,
85+
layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32),
8386
AtomicWriteOp::Relaxed,
8487
)
8588
}
@@ -100,7 +103,7 @@ fn mutex_set_id<'mir, 'tcx: 'mir>(
100103
mutex_op,
101104
4,
102105
id,
103-
ecx.machine.layouts.u32,
106+
layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
104107
AtomicWriteOp::Relaxed,
105108
)
106109
}
@@ -144,7 +147,7 @@ fn rwlock_set_id<'mir, 'tcx: 'mir>(
144147
rwlock_op,
145148
4,
146149
id,
147-
ecx.machine.layouts.u32,
150+
layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
148151
AtomicWriteOp::Relaxed,
149152
)
150153
}
@@ -211,7 +214,7 @@ fn cond_set_id<'mir, 'tcx: 'mir>(
211214
cond_op,
212215
4,
213216
id,
214-
ecx.machine.layouts.u32,
217+
layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.u32),
215218
AtomicWriteOp::Relaxed,
216219
)
217220
}
@@ -244,7 +247,12 @@ fn cond_set_clock_id<'mir, 'tcx: 'mir>(
244247
cond_op: &OpTy<'tcx, Tag>,
245248
clock_id: impl Into<ScalarMaybeUninit<Tag>>,
246249
) -> InterpResult<'tcx, ()> {
247-
ecx.write_scalar_at_offset(cond_op, 8, clock_id, ecx.machine.layouts.i32)
250+
ecx.write_scalar_at_offset(
251+
cond_op,
252+
8,
253+
clock_id,
254+
layout_of_maybe_uninit(ecx.tcx, ecx.tcx.types.i32),
255+
)
248256
}
249257

250258
/// 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
788796
Ok(0)
789797
}
790798
}
799+
800+
fn layout_of_maybe_uninit<'tcx>(tcx: TyCtxtAt<'tcx>, param: Ty<'tcx>) -> TyAndLayout<'tcx> {
801+
let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None);
802+
let def_ty = tcx.type_of(def_id);
803+
let ty = def_ty.subst(*tcx, &[param.into()]);
804+
805+
let param_env = tcx.param_env(def_id);
806+
tcx.layout_of(param_env.and(ty)).unwrap()
807+
}

tests/compile-fail/uninit_float.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -Zmiri-check-number-validity
2+
3+
// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
4+
5+
fn main() {
6+
let _val = unsafe { std::mem::MaybeUninit::<f32>::uninit().assume_init() };
7+
//~^ ERROR type validation failed at .value: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes
8+
}

tests/compile-fail/uninit_integer.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -Zmiri-check-number-validity
2+
3+
// This test is from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
4+
5+
fn main() {
6+
let _val = unsafe { std::mem::MaybeUninit::<usize>::uninit().assume_init() };
7+
//~^ ERROR type validation failed at .value: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: -Zmiri-check-number-validity
2+
3+
// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
4+
5+
fn main() {
6+
let _val = unsafe { std::mem::MaybeUninit::<i32>::uninit().assume_init() };
7+
//~^ ERROR type validation failed at .value: encountered uninitialized bytes, but expected initialized plain (non-pointer) bytes
8+
}

tests/compile-fail/uninit_raw_ptr.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
let _val = unsafe { std::mem::MaybeUninit::<*const u8>::uninit().assume_init() };
3+
//~^ ERROR type validation failed at .value: encountered uninitialized raw pointer
4+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
2+
// This test passes because -Zmiri-check-number-validity is not passed.
3+
4+
fn main() {
5+
let _val1 = unsafe { std::mem::MaybeUninit::<usize>::uninit().assume_init() };
6+
let _val2 = unsafe { std::mem::MaybeUninit::<i32>::uninit().assume_init() };
7+
let _val3 = unsafe { std::mem::MaybeUninit::<f32>::uninit().assume_init() };
8+
}

0 commit comments

Comments
 (0)