Skip to content

Commit cf47776

Browse files
committed
Auto merge of #2151 - RalfJung:numbers, r=oli-obk
enable number validity checking and ptr::invalid checking by default This removes the `-Zmiri-check-number-validity` flag, enabling its effects by default. (We don't error when the flag is passed, for backwards compatibility.) We also enable by default that transmuting an integer to a pointer now creates a pointer with `None` provenance, which is invalid to dereference (and, in the case of a function pointer, invalid to call). I did this together since it is all related to ptr2int/int2ptr transmutation. Two new flags are added to optionally take back these stricter checks: - `-Zmiri-allow-uninit-numbers` makes Miri accept uninit data in integers and floats - `-Zmiri-allow-ptr-int-transmute` makes Miri accept pointers (provenance data) in integers and floats, *and* makes Miri treat int2ptr transmutes as equivalent to a cast. The flag names make sense IMO, but they are somewhat inconsistent with our existing flags since we usually call things `-Zmiri-disable-$CHECK` rather than `-Zmiri-allow-$THING`. But `-Zmiri-disable-uninit-number-check` sounds silly? (Whenever I say "transmute" this includes union and pointer based type punning.) Cc `@saethlin` I hope this won't break everything?^^ I think the most risky part is the int2ptr transmute aspect, in particular around function pointers where no `as` casts are possible. The correct pattern is to first cast to a raw ptr and then transmute that to a fn ptr. We should probably document this better, in the `transmute` documentation and maybe in the documentation for the `fn()` type. I should run this PR against the std test suite before we land it. r? `@oli-obk` - [x] Ensure stdlib docs recommend "usize -> raw ptr -> fn ptr" for int-to-fnptr casts: rust-lang/rust#97321 - [x] Run the stdlib test suite
2 parents 0a4279f + 8c42ef1 commit cf47776

27 files changed

+67
-91
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ in your program, and cannot run all programs:
4444
positives here, so if your program runs fine in Miri right now that is by no
4545
means a guarantee that it is UB-free when these questions get answered.
4646

47-
In particular, Miri does currently not check that integers/floats are
48-
initialized or that references point to valid data.
47+
In particular, Miri does currently not check that references point to valid data.
4948
* If the program relies on unspecified details of how data is laid out, it will
5049
still run fine in Miri -- but might break (including causing UB) on different
5150
compiler versions or different platforms.
@@ -302,10 +301,15 @@ The remaining flags are for advanced use only, and more likely to change or be r
302301
Some of these are **unsound**, which means they can lead
303302
to Miri failing to detect cases of undefined behavior in a program.
304303

305-
* `-Zmiri-check-number-validity` enables checking of integer and float validity
306-
(e.g., they must be initialized and not carry pointer provenance) as part of
307-
enforcing validity invariants. This has no effect when
304+
* `-Zmiri-allow-uninit-numbers` disables the check to ensure that number types (integer and float
305+
types) always hold initialized data. (They must still be initialized when any actual operation,
306+
such as arithmetic, is performed.) Using this flag is **unsound**. This has no effect when
308307
`-Zmiri-disable-validation` is present.
308+
* `-Zmiri-allow-ptr-int-transmute` makes Miri more accepting of transmutation between pointers and
309+
integers via `mem::transmute` or union/pointer type punning. This has two effects: it disables the
310+
check against integers storing a pointer (i.e., data with provenance), thus allowing
311+
pointer-to-integer transmutation, and it treats integer-to-pointer transmutation as equivalent to
312+
a cast. Using this flag is **unsound**.
309313
* `-Zmiri-disable-abi-check` disables checking [function ABI]. Using this flag
310314
is **unsound**.
311315
* `-Zmiri-disable-alignment-check` disables checking pointer alignment, so you

src/bin/miri.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,16 @@ fn main() {
335335
miri_config.check_alignment = miri::AlignmentCheck::Symbolic;
336336
}
337337
"-Zmiri-check-number-validity" => {
338-
miri_config.check_number_validity = true;
338+
eprintln!(
339+
"WARNING: the flag `-Zmiri-check-number-validity` no longer has any effect \
340+
since it is now enabled by default"
341+
);
342+
}
343+
"-Zmiri-allow-uninit-numbers" => {
344+
miri_config.allow_uninit_numbers = true;
345+
}
346+
"-Zmiri-allow-ptr-int-transmute" => {
347+
miri_config.allow_ptr_int_transmute = true;
339348
}
340349
"-Zmiri-disable-abi-check" => {
341350
miri_config.check_abi = false;
@@ -386,7 +395,6 @@ fn main() {
386395
"-Zmiri-strict-provenance" => {
387396
miri_config.provenance_mode = ProvenanceMode::Strict;
388397
miri_config.tag_raw = true;
389-
miri_config.check_number_validity = true;
390398
}
391399
"-Zmiri-permissive-provenance" => {
392400
miri_config.provenance_mode = ProvenanceMode::Permissive;

src/eval.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ pub struct MiriConfig {
7777
pub stacked_borrows: bool,
7878
/// Controls alignment checking.
7979
pub check_alignment: AlignmentCheck,
80-
/// Controls integer and float validity (e.g., initialization) checking.
81-
pub check_number_validity: bool,
80+
/// Controls integer and float validity initialization checking.
81+
pub allow_uninit_numbers: bool,
82+
/// Controls how we treat ptr2int and int2ptr transmutes.
83+
pub allow_ptr_int_transmute: bool,
8284
/// Controls function [ABI](Abi) checking.
8385
pub check_abi: bool,
8486
/// Action for an op requiring communication with the host.
@@ -126,7 +128,8 @@ impl Default for MiriConfig {
126128
validate: true,
127129
stacked_borrows: true,
128130
check_alignment: AlignmentCheck::Int,
129-
check_number_validity: false,
131+
allow_uninit_numbers: false,
132+
allow_ptr_int_transmute: false,
130133
check_abi: true,
131134
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
132135
ignore_leaks: false,

src/intptrcast.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,12 @@ impl<'mir, 'tcx> GlobalStateInner {
118118
) -> Pointer<Option<Tag>> {
119119
trace!("Transmuting 0x{:x} to a pointer", addr);
120120

121-
let global_state = ecx.machine.intptrcast.borrow();
122-
123-
match global_state.provenance_mode {
124-
ProvenanceMode::Legacy => {
125-
// In legacy mode, we have to support int2ptr transmutes,
126-
// so just pretend they do the same thing as a cast.
127-
Self::ptr_from_addr_cast(ecx, addr)
128-
}
129-
ProvenanceMode::Permissive | ProvenanceMode::Strict => {
130-
// Both of these modes consider transmuted pointers to be "invalid" (`None`
131-
// provenance).
132-
Pointer::new(None, Size::from_bytes(addr))
133-
}
121+
if ecx.machine.allow_ptr_int_transmute {
122+
// When we allow transmutes, treat them like casts.
123+
Self::ptr_from_addr_cast(ecx, addr)
124+
} else {
125+
// We consider transmuted pointers to be "invalid" (`None` provenance).
126+
Pointer::new(None, Size::from_bytes(addr))
134127
}
135128
}
136129

src/machine.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,13 +255,19 @@ pub struct Evaluator<'mir, 'tcx> {
255255
/// Whether to enforce the validity invariant.
256256
pub(crate) validate: bool,
257257

258-
/// Whether to enforce validity (e.g., initialization) of integers and floats.
259-
pub(crate) enforce_number_validity: bool,
258+
/// Whether to allow uninitialized numbers (integers and floats).
259+
pub(crate) allow_uninit_numbers: bool,
260+
261+
/// Whether to allow ptr2int transmutes, and whether to allow *dereferencing* the result of an
262+
/// int2ptr transmute.
263+
pub(crate) allow_ptr_int_transmute: bool,
260264

261265
/// Whether to enforce [ABI](Abi) of function calls.
262266
pub(crate) enforce_abi: bool,
263267

268+
/// The table of file descriptors.
264269
pub(crate) file_handler: shims::posix::FileHandler,
270+
/// The table of directory descriptors.
265271
pub(crate) dir_handler: shims::posix::DirHandler,
266272

267273
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
@@ -351,7 +357,8 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
351357
tls: TlsData::default(),
352358
isolated_op: config.isolated_op,
353359
validate: config.validate,
354-
enforce_number_validity: config.check_number_validity,
360+
allow_uninit_numbers: config.allow_uninit_numbers,
361+
allow_ptr_int_transmute: config.allow_ptr_int_transmute,
355362
enforce_abi: config.check_abi,
356363
file_handler: FileHandler::new(config.mute_stdout_stderr),
357364
dir_handler: Default::default(),
@@ -493,12 +500,12 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
493500

494501
#[inline(always)]
495502
fn enforce_number_init(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
496-
ecx.machine.enforce_number_validity
503+
!ecx.machine.allow_uninit_numbers
497504
}
498505

499506
#[inline(always)]
500507
fn enforce_number_no_provenance(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
501-
ecx.machine.enforce_number_validity
508+
!ecx.machine.allow_ptr_int_transmute
502509
}
503510

504511
#[inline(always)]

src/shims/posix/sync.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
405405

406406
// To catch double-destroys, we de-initialize the mutexattr.
407407
// This is technically not right and might lead to false positives. For example, the below
408-
// code is *likely* sound, even assuming uninit numbers are UB, but miri with
409-
// -Zmiri-check-number-validity complains
408+
// code is *likely* sound, even assuming uninit numbers are UB, but Miri complains.
410409
//
411410
// let mut x: MaybeUninit<libc::pthread_mutexattr_t> = MaybeUninit::zeroed();
412411
// libc::pthread_mutexattr_init(x.as_mut_ptr());

tests/compile-fail/provenance/ptr_int_unexposed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// compile-flags: -Zmiri-permissive-provenance -Zmiri-disable-stacked-borrows
1+
// compile-flags: -Zmiri-permissive-provenance -Zmiri-disable-stacked-borrows -Zmiri-allow-ptr-int-transmute
22

33
fn main() {
44
let x: i32 = 3;

tests/compile-fail/provenance/ptr_invalid.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// compile-flags: -Zmiri-permissive-provenance
21
#![feature(strict_provenance)]
32

43
// Ensure that a `ptr::invalid` ptr is truly invalid.

tests/compile-fail/stacked_borrows/illegal_read3.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// compile-flags: -Zmiri-allow-ptr-int-transmute
12
// A callee may not read the destination of our `&mut` without us noticing.
23
// Thise code got carefully checked to not introduce any reborrows
34
// that are not explicit in the source. Let's hope the compiler does not break this later!

tests/compile-fail/transmute-pair-uninit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// compile-flags: -Zmiri-allow-uninit-numbers
12
#![feature(core_intrinsics)]
23

34
use std::mem;

0 commit comments

Comments
 (0)