Skip to content

Commit 5832dd1

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 5832dd1

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;

tests/compile-fail/uninit_byte_read.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
fn main() {
23
let v: Vec<u8> = Vec::with_capacity(10);
34
let undef = unsafe { *v.get_unchecked(5) };

tests/compile-fail/validity/invalid_enum_tag_256variants_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
#![allow(unused, deprecated, invalid_value)]
23

34
#[derive(Copy, Clone)]

tests/compile-fail/validity/ptr_integer_array_transmute.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// compile-flags: -Zmiri-check-number-validity
2-
31
fn main() {
42
let r = &mut 42;
53
let _i: [usize; 1] = unsafe { std::mem::transmute(r) }; //~ ERROR encountered a pointer, but expected plain (non-pointer) bytes

tests/compile-fail/validity/ptr_integer_transmute.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// compile-flags: -Zmiri-check-number-validity
2-
31
fn main() {
42
let r = &mut 42;
53
let _i: usize = unsafe { std::mem::transmute(r) }; //~ ERROR expected plain (non-pointer) bytes

tests/compile-fail/validity/uninit_float.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// compile-flags: -Zmiri-check-number-validity
2-
31
// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
42

53
fn main() {

tests/compile-fail/validity/uninit_integer.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// compile-flags: -Zmiri-check-number-validity
2-
31
// This test is from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
42

53
fn main() {

tests/compile-fail/validity/uninit_integer_signed.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// compile-flags: -Zmiri-check-number-validity
2-
31
// This test is adapted from https://github.com/rust-lang/miri/issues/1340#issue-600900312.
42

53
fn main() {

tests/run-pass/bitop-beyond-alignment.rs

Lines changed: 0 additions & 36 deletions
This file was deleted.

tests/run-pass/concurrency/libc_pthread_cond.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// ignore-windows: No libc on Windows
22
// ignore-apple: pthread_condattr_setclock is not supported on MacOS.
3-
// compile-flags: -Zmiri-disable-isolation -Zmiri-check-number-validity
3+
// compile-flags: -Zmiri-disable-isolation
44

55
#![feature(rustc_private)]
66

tests/run-pass/intptrcast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// compile-flags: -Zmiri-allow-ptr-int-transmute
2+
13
// This returns a miri pointer at type usize, if the argument is a proper pointer
24
fn transmute_ptr_to_int<T>(x: *const T) -> usize {
35
unsafe { std::mem::transmute(x) }

tests/run-pass/libc.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,17 +197,17 @@ fn test_prctl_thread_name() {
197197
use libc::c_long;
198198
unsafe {
199199
let mut buf = [255; 10];
200-
assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0);
200+
assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long), 0);
201201
assert_eq!(b"<unnamed>\0", &buf);
202202
let thread_name = CString::new("hello").expect("CString::new failed");
203-
assert_eq!(libc::prctl(libc::PR_SET_NAME, thread_name.as_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0);
203+
assert_eq!(libc::prctl(libc::PR_SET_NAME, thread_name.as_ptr(), 0 as c_long, 0 as c_long, 0 as c_long), 0);
204204
let mut buf = [255; 6];
205-
assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0);
205+
assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long), 0);
206206
assert_eq!(b"hello\0", &buf);
207207
let long_thread_name = CString::new("01234567890123456789").expect("CString::new failed");
208-
assert_eq!(libc::prctl(libc::PR_SET_NAME, long_thread_name.as_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0);
208+
assert_eq!(libc::prctl(libc::PR_SET_NAME, long_thread_name.as_ptr(), 0 as c_long, 0 as c_long, 0 as c_long), 0);
209209
let mut buf = [255; 16];
210-
assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr() as c_long, 0 as c_long, 0 as c_long, 0 as c_long), 0);
210+
assert_eq!(libc::prctl(libc::PR_GET_NAME, buf.as_mut_ptr(), 0 as c_long, 0 as c_long, 0 as c_long), 0);
211211
assert_eq!(b"012345678901234\0", &buf);
212212
}
213213
}

tests/run-pass/move-uninit-primval.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
#![allow(deprecated)]
23

34
struct Foo {

tests/run-pass/partially-uninit.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// compile-flags: -Zmiri-check-number-validity
2-
31
use std::mem::{self, MaybeUninit};
42

53
#[repr(C)]

tests/run-pass/ptr_offset.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ fn ptr_offset() {
6161
unsafe {
6262
let p = f as fn() -> i32 as usize;
6363
let x = (p as *mut u32).offset(0) as usize;
64-
let f: fn() -> i32 = mem::transmute(x);
64+
// *cast* to ptr, then transmute to fn ptr.
65+
// (transmuting int to [fn]ptr causes trouble.)
66+
let f: fn() -> i32 = mem::transmute(x as *const ());
6567
assert_eq!(f(), 42);
6668
}
6769
}

tests/run-pass/tag-align-dyn-u64.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ fn mk_rec() -> Rec {
2525
}
2626

2727
fn is_u64_aligned(u: &Tag<u64>) -> bool {
28-
let p: usize = unsafe { mem::transmute(u) };
28+
let p: *const () = unsafe { mem::transmute(u) };
29+
let p = p as usize;
2930
let u64_align = std::mem::align_of::<u64>();
3031
return (p & (u64_align - 1)) == 0;
3132
}

tests/run-pass/transmute_fat.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// Stacked Borrows disallows this becuase the reference is never cast to a raw pointer.
2-
// compile-flags: -Zmiri-disable-stacked-borrows
2+
// compile-flags: -Zmiri-disable-stacked-borrows -Zmiri-allow-ptr-int-transmute
33

44
fn main() {
55
// If we are careful, we can exploit data layout...
66
let raw = unsafe {
77
std::mem::transmute::<&[u8], [usize; 2]>(&[42])
88
};
99
let ptr = raw[0] + raw[1];
10-
let ptr = ptr as *const u8;
10+
// We transmute both ways, to really test allow-ptr-int-transmute.
11+
let ptr: *const u8 = unsafe { std::mem::transmute(ptr) };
1112
// The pointer is one-past-the end, but we decrement it into bounds before using it
1213
assert_eq!(unsafe { *ptr.offset(-1) }, 42);
1314
}

tests/run-pass/uninit_number_ignored.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
// compile-flags: -Zmiri-allow-uninit-numbers
12
// 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.
33

44
fn main() {
55
let _val1 = unsafe { std::mem::MaybeUninit::<usize>::uninit().assume_init() };

0 commit comments

Comments
 (0)