Skip to content

Commit 5327179

Browse files
committed
const validation: better error for maybe-null references
1 parent 87332ab commit 5327179

File tree

9 files changed

+108
-29
lines changed

9 files changed

+108
-29
lines changed

compiler/rustc_const_eval/messages.ftl

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,15 @@ const_eval_validation_invalid_vtable_trait = {$front_matter}: wrong trait in wid
476476
const_eval_validation_mutable_ref_in_const = {$front_matter}: encountered mutable reference in `const` value
477477
const_eval_validation_mutable_ref_to_immutable = {$front_matter}: encountered mutable reference or box pointing to read-only memory
478478
const_eval_validation_never_val = {$front_matter}: encountered a value of the never type `!`
479-
const_eval_validation_null_box = {$front_matter}: encountered a null box
479+
const_eval_validation_null_box = {$front_matter}: encountered a {$maybe ->
480+
[true] maybe-null
481+
*[false] null
482+
} box
480483
const_eval_validation_null_fn_ptr = {$front_matter}: encountered a null function pointer
481-
const_eval_validation_null_ref = {$front_matter}: encountered a null reference
484+
const_eval_validation_null_ref = {$front_matter}: encountered a {$maybe ->
485+
[true] maybe-null
486+
*[false] null
487+
} reference
482488
const_eval_validation_nonnull_ptr_out_of_range = {$front_matter}: encountered a maybe-null pointer, but expected something that is definitely non-zero
483489
const_eval_validation_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range}
484490
const_eval_validation_partial_pointer = {$front_matter}: encountered a partial pointer or a mix of pointers

compiler/rustc_const_eval/src/errors.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -696,8 +696,8 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
696696
}
697697
UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_unaligned_box,
698698

699-
NullPtr { ptr_kind: PointerKind::Box } => const_eval_validation_null_box,
700-
NullPtr { ptr_kind: PointerKind::Ref(_) } => const_eval_validation_null_ref,
699+
NullPtr { ptr_kind: PointerKind::Box, .. } => const_eval_validation_null_box,
700+
NullPtr { ptr_kind: PointerKind::Ref(_), .. } => const_eval_validation_null_ref,
701701
DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => {
702702
const_eval_validation_dangling_box_no_provenance
703703
}
@@ -820,8 +820,10 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
820820
err.arg("vtable_dyn_type", vtable_dyn_type.to_string());
821821
err.arg("expected_dyn_type", expected_dyn_type.to_string());
822822
}
823-
NullPtr { .. }
824-
| MutableRefToImmutable
823+
NullPtr { maybe, .. } => {
824+
err.arg("maybe", maybe);
825+
}
826+
MutableRefToImmutable
825827
| MutableRefInConst
826828
| NullFnPtr
827829
| NonnullPtrMaybeNull

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
511511
CheckInAllocMsg::Dereferenceable, // will anyway be replaced by validity message
512512
),
513513
self.path,
514-
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind },
514+
Ub(DanglingIntPointer { addr: 0, .. }) => NullPtr { ptr_kind, maybe: false },
515515
Ub(DanglingIntPointer { addr: i, .. }) => DanglingPtrNoProvenance {
516516
ptr_kind,
517517
// FIXME this says "null pointer" when null but we need translate
@@ -538,8 +538,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
538538
);
539539
// Make sure this is non-null. We checked dereferenceability above, but if `size` is zero
540540
// that does not imply non-null.
541-
if self.ecx.scalar_may_be_null(Scalar::from_maybe_pointer(place.ptr(), self.ecx))? {
542-
throw_validation_failure!(self.path, NullPtr { ptr_kind })
541+
let scalar = Scalar::from_maybe_pointer(place.ptr(), self.ecx);
542+
if self.ecx.scalar_may_be_null(scalar)? {
543+
let maybe = !M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..));
544+
throw_validation_failure!(self.path, NullPtr { ptr_kind, maybe })
543545
}
544546
// Do not allow references to uninhabited types.
545547
if place.layout.is_uninhabited() {
@@ -757,6 +759,11 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
757759
} else {
758760
// Otherwise (for standalone Miri), we have to still check it to be non-null.
759761
if self.ecx.scalar_may_be_null(scalar)? {
762+
let maybe =
763+
!M::Provenance::OFFSET_IS_ADDR && matches!(scalar, Scalar::Ptr(..));
764+
// This can't be a "maybe-null" pointer since the check for this being
765+
// a fn ptr at all already ensures that the pointer is inbounds.
766+
assert!(!maybe);
760767
throw_validation_failure!(self.path, NullFnPtr);
761768
}
762769
}

compiler/rustc_middle/src/mir/interpret/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ pub enum ValidationErrorKind<'tcx> {
541541
},
542542
NullPtr {
543543
ptr_kind: PointerKind,
544+
/// Records whether this pointer is definitely null or just may be null.
545+
maybe: bool,
544546
},
545547
DanglingPtrNoProvenance {
546548
ptr_kind: PointerKind,

src/tools/compiletest/src/runtest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2655,7 +2655,7 @@ impl<'test> TestCx<'test> {
26552655

26562656
// The alloc-id appears in pretty-printed allocations.
26572657
normalized = static_regex!(
2658-
r"╾─*a(lloc)?([0-9]+)(\+0x[0-9]+)?(<imm>)?( \([0-9]+ ptr bytes\))?─*╼"
2658+
r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?(<imm>)?( \([0-9]+ ptr bytes\))?─*╼"
26592659
)
26602660
.replace_all(&normalized, |caps: &Captures<'_>| {
26612661
// Renumber the captured index.

tests/ui/consts/const-eval/ub-ref-ptr.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ const NULL: &u16 = unsafe { mem::transmute(0usize) };
2727
const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) };
2828
//~^ ERROR invalid value
2929

30+
const MAYBE_NULL_BOX: Box<()> = unsafe { mem::transmute({
31+
//~^ ERROR maybe-null
32+
let ref_ = &0u8;
33+
(ref_ as *const u8).wrapping_add(10)
34+
}) };
3035

3136
// It is very important that we reject this: We do promote `&(4 * REF_AS_USIZE)`,
3237
// but that would fail to compile; so we ended up breaking user code that would
@@ -57,7 +62,12 @@ const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
5762
//~^ ERROR invalid value
5863
const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
5964
//~^ ERROR invalid value
60-
65+
const MAYBE_NULL_FN_PTR: fn() = unsafe { mem::transmute({
66+
//~^ ERROR invalid value
67+
fn fun() {}
68+
let ptr = fun as fn();
69+
(ptr as *const u8).wrapping_add(10)
70+
}) };
6171

6272
const UNALIGNED_READ: () = unsafe {
6373
let x = &[0u8; 4];

tests/ui/consts/const-eval/ub-ref-ptr.stderr

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,19 @@ LL | const NULL_BOX: Box<u16> = unsafe { mem::transmute(0usize) };
4242
HEX_DUMP
4343
}
4444

45+
error[E0080]: constructing invalid value: encountered a maybe-null box
46+
--> $DIR/ub-ref-ptr.rs:30:1
47+
|
48+
LL | const MAYBE_NULL_BOX: Box<()> = unsafe { mem::transmute({
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
50+
|
51+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
52+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
53+
HEX_DUMP
54+
}
55+
4556
error[E0080]: unable to turn pointer into integer
46-
--> $DIR/ub-ref-ptr.rs:34:1
57+
--> $DIR/ub-ref-ptr.rs:39:1
4758
|
4859
LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
4960
| ^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE` failed here
@@ -52,7 +63,7 @@ LL | const REF_AS_USIZE: usize = unsafe { mem::transmute(&0) };
5263
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
5364

5465
error[E0080]: unable to turn pointer into integer
55-
--> $DIR/ub-ref-ptr.rs:37:39
66+
--> $DIR/ub-ref-ptr.rs:42:39
5667
|
5768
LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
5869
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE_SLICE` failed here
@@ -61,13 +72,13 @@ LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
6172
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
6273

6374
note: erroneous constant encountered
64-
--> $DIR/ub-ref-ptr.rs:37:38
75+
--> $DIR/ub-ref-ptr.rs:42:38
6576
|
6677
LL | const REF_AS_USIZE_SLICE: &[usize] = &[unsafe { mem::transmute(&0) }];
6778
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6879

6980
error[E0080]: unable to turn pointer into integer
70-
--> $DIR/ub-ref-ptr.rs:40:86
81+
--> $DIR/ub-ref-ptr.rs:45:86
7182
|
7283
LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
7384
| ^^^^^^^^^^^^^^^^^^^^ evaluation of `REF_AS_USIZE_BOX_SLICE` failed here
@@ -76,13 +87,13 @@ LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[us
7687
= help: the absolute address of a pointer is not known at compile-time, so such operations are not supported
7788

7889
note: erroneous constant encountered
79-
--> $DIR/ub-ref-ptr.rs:40:85
90+
--> $DIR/ub-ref-ptr.rs:45:85
8091
|
8192
LL | const REF_AS_USIZE_BOX_SLICE: Box<[usize]> = unsafe { mem::transmute::<&[usize], _>(&[mem::transmute(&0)]) };
8293
| ^^^^^^^^^^^^^^^^^^^^^
8394

8495
error[E0080]: constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance)
85-
--> $DIR/ub-ref-ptr.rs:43:1
96+
--> $DIR/ub-ref-ptr.rs:48:1
8697
|
8798
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
8899
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
@@ -93,7 +104,7 @@ LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
93104
}
94105

95106
error[E0080]: constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance)
96-
--> $DIR/ub-ref-ptr.rs:46:1
107+
--> $DIR/ub-ref-ptr.rs:51:1
97108
|
98109
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
99110
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
@@ -103,8 +114,8 @@ LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
103114
HEX_DUMP
104115
}
105116

106-
error[E0080]: reading memory at ALLOC4[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory
107-
--> $DIR/ub-ref-ptr.rs:49:41
117+
error[E0080]: reading memory at ALLOC6[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory
118+
--> $DIR/ub-ref-ptr.rs:54:41
108119
|
109120
LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init };
110121
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT_PTR` failed here
@@ -114,7 +125,7 @@ LL | const UNINIT_PTR: *const i32 = unsafe { MaybeUninit { uninit: () }.init };
114125
}
115126

116127
error[E0080]: constructing invalid value: encountered null pointer, but expected a function pointer
117-
--> $DIR/ub-ref-ptr.rs:52:1
128+
--> $DIR/ub-ref-ptr.rs:57:1
118129
|
119130
LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
120131
| ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
@@ -124,8 +135,8 @@ LL | const NULL_FN_PTR: fn() = unsafe { mem::transmute(0usize) };
124135
HEX_DUMP
125136
}
126137

127-
error[E0080]: reading memory at ALLOC5[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory
128-
--> $DIR/ub-ref-ptr.rs:54:38
138+
error[E0080]: reading memory at ALLOC7[0x%..0x%], but memory is uninitialized at [0x%..0x%], and this operation requires initialized memory
139+
--> $DIR/ub-ref-ptr.rs:59:38
129140
|
130141
LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init };
131142
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `UNINIT_FN_PTR` failed here
@@ -135,7 +146,7 @@ LL | const UNINIT_FN_PTR: fn() = unsafe { MaybeUninit { uninit: () }.init };
135146
}
136147

137148
error[E0080]: constructing invalid value: encountered 0xd[noalloc], but expected a function pointer
138-
--> $DIR/ub-ref-ptr.rs:56:1
149+
--> $DIR/ub-ref-ptr.rs:61:1
139150
|
140151
LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
141152
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
@@ -145,8 +156,8 @@ LL | const DANGLING_FN_PTR: fn() = unsafe { mem::transmute(13usize) };
145156
HEX_DUMP
146157
}
147158

148-
error[E0080]: constructing invalid value: encountered ALLOC2<imm>, but expected a function pointer
149-
--> $DIR/ub-ref-ptr.rs:58:1
159+
error[E0080]: constructing invalid value: encountered ALLOC3<imm>, but expected a function pointer
160+
--> $DIR/ub-ref-ptr.rs:63:1
150161
|
151162
LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
152163
| ^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
@@ -156,14 +167,25 @@ LL | const DATA_FN_PTR: fn() = unsafe { mem::transmute(&13) };
156167
HEX_DUMP
157168
}
158169

170+
error[E0080]: constructing invalid value: encountered ALLOC4+0xa, but expected a function pointer
171+
--> $DIR/ub-ref-ptr.rs:65:1
172+
|
173+
LL | const MAYBE_NULL_FN_PTR: fn() = unsafe { mem::transmute({
174+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
175+
|
176+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
177+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
178+
HEX_DUMP
179+
}
180+
159181
error[E0080]: accessing memory based on pointer with alignment 1, but alignment 4 is required
160-
--> $DIR/ub-ref-ptr.rs:65:5
182+
--> $DIR/ub-ref-ptr.rs:75:5
161183
|
162184
LL | ptr.read();
163185
| ^^^^^^^^^^ evaluation of `UNALIGNED_READ` failed here
164186

165187
error[E0080]: constructing invalid value: encountered a pointer with unknown absolute address, but expected something that is definitely greater or equal to 1000
166-
--> $DIR/ub-ref-ptr.rs:74:1
188+
--> $DIR/ub-ref-ptr.rs:84:1
167189
|
168190
LL | const INVALID_VALUE_PTR: High = unsafe { mem::transmute(&S) };
169191
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
@@ -173,6 +195,6 @@ LL | const INVALID_VALUE_PTR: High = unsafe { mem::transmute(&S) };
173195
HEX_DUMP
174196
}
175197

176-
error: aborting due to 16 previous errors
198+
error: aborting due to 18 previous errors
177199

178200
For more information about this error, try `rustc --explain E0080`.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//! Ensure a decent error message for maybe-null references.
2+
//! (see <https://github.com/rust-lang/rust/issues/146748>)
3+
4+
// Strip out raw byte dumps to make comparison platform-independent:
5+
//@ normalize-stderr: "(the raw bytes of the constant) \(size: [0-9]*, align: [0-9]*\)" -> "$1 (size: $$SIZE, align: $$ALIGN)"
6+
//@ normalize-stderr: "([0-9a-f][0-9a-f] |╾─*A(LLOC)?[0-9]+(\+[a-z0-9]+)?(<imm>)?─*╼ )+ *│.*" -> "HEX_DUMP"
7+
8+
#![feature(const_trait_impl, const_cmp)]
9+
10+
use std::any::TypeId;
11+
use std::mem::transmute;
12+
13+
const A: [&(); 2] = unsafe { transmute(TypeId::of::<i32>()) };
14+
//~^ERROR: maybe-null
15+
16+
fn main() {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0080]: constructing invalid value at [0]: encountered a maybe-null reference
2+
--> $DIR/const_transmute_type_id7.rs:13:1
3+
|
4+
LL | const A: [&(); 2] = unsafe { transmute(TypeId::of::<i32>()) };
5+
| ^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
6+
|
7+
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
8+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
9+
HEX_DUMP
10+
}
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)