Skip to content

Commit 131a755

Browse files
authored
Rollup merge of rust-lang#55894 - RalfJung:validation-enums, r=oli-obk
miri enum discriminant handling: Fix treatment of pointers, better error when it is undef r? @oli-obk
2 parents 3aeac24 + b8915f2 commit 131a755

15 files changed

+107
-67
lines changed

src/librustc/ich/impls_ty.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ impl_stable_hash_for!(
451451
FunctionRetMismatch(a, b),
452452
NoMirFor(s),
453453
UnterminatedCString(ptr),
454-
PointerOutOfBounds { ptr, access, allocation_size },
454+
PointerOutOfBounds { ptr, check, allocation_size },
455455
InvalidBoolOp(bop),
456456
Unimplemented(s),
457457
BoundsCheck { len, index },
@@ -471,6 +471,11 @@ impl_stable_hash_for!(
471471
}
472472
);
473473

474+
impl_stable_hash_for!(enum mir::interpret::InboundsCheck {
475+
Live,
476+
MaybeDead
477+
});
478+
474479
impl_stable_hash_for!(enum mir::interpret::Lock {
475480
NoLock,
476481
WriteLock(dl),

src/librustc/mir/interpret/allocation.rs

+8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ use mir;
1919
use std::ops::{Deref, DerefMut};
2020
use rustc_data_structures::sorted_map::SortedMap;
2121

22+
/// Used by `check_bounds` to indicate whether the pointer needs to be just inbounds
23+
/// or also inbounds of a *live* allocation.
24+
#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable)]
25+
pub enum InboundsCheck {
26+
Live,
27+
MaybeDead,
28+
}
29+
2230
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
2331
pub struct Allocation<Tag=(),Extra=()> {
2432
/// The actual bytes of the allocation.

src/librustc/mir/interpret/error.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use ty::{Ty, layout};
1515
use ty::layout::{Size, Align, LayoutError};
1616
use rustc_target::spec::abi::Abi;
1717

18-
use super::{Pointer, Scalar};
18+
use super::{Pointer, InboundsCheck, ScalarMaybeUndef};
1919

2020
use backtrace::Backtrace;
2121

@@ -240,10 +240,10 @@ pub enum EvalErrorKind<'tcx, O> {
240240
InvalidMemoryAccess,
241241
InvalidFunctionPointer,
242242
InvalidBool,
243-
InvalidDiscriminant(Scalar),
243+
InvalidDiscriminant(ScalarMaybeUndef),
244244
PointerOutOfBounds {
245245
ptr: Pointer,
246-
access: bool,
246+
check: InboundsCheck,
247247
allocation_size: Size,
248248
},
249249
InvalidNullPointerUsage,
@@ -457,9 +457,13 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
457457
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458458
use self::EvalErrorKind::*;
459459
match *self {
460-
PointerOutOfBounds { ptr, access, allocation_size } => {
461-
write!(f, "{} at offset {}, outside bounds of allocation {} which has size {}",
462-
if access { "memory access" } else { "pointer computed" },
460+
PointerOutOfBounds { ptr, check, allocation_size } => {
461+
write!(f, "Pointer must be in-bounds{} at offset {}, but is outside bounds of \
462+
allocation {} which has size {}",
463+
match check {
464+
InboundsCheck::Live => " and live",
465+
InboundsCheck::MaybeDead => "",
466+
},
463467
ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
464468
},
465469
ValidationFailure(ref err) => {

src/librustc/mir/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub use self::error::{
2828
pub use self::value::{Scalar, ConstValue, ScalarMaybeUndef};
2929

3030
pub use self::allocation::{
31-
Allocation, AllocationExtra,
31+
InboundsCheck, Allocation, AllocationExtra,
3232
Relocations, UndefMask,
3333
};
3434

src/librustc_mir/interpret/memory.rs

+27-23
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc_data_structures::fx::{FxHashSet, FxHashMap};
2828
use syntax::ast::Mutability;
2929

3030
use super::{
31-
Pointer, AllocId, Allocation, ConstValue, GlobalId, AllocationExtra,
31+
Pointer, AllocId, Allocation, ConstValue, GlobalId, AllocationExtra, InboundsCheck,
3232
EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
3333
Machine, AllocMap, MayLeak, ScalarMaybeUndef, ErrorHandled,
3434
};
@@ -249,17 +249,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
249249
// Check non-NULL/Undef, extract offset
250250
let (offset, alloc_align) = match ptr {
251251
Scalar::Ptr(ptr) => {
252-
let (size, align) = self.get_size_and_align(ptr.alloc_id);
253252
// check this is not NULL -- which we can ensure only if this is in-bounds
254253
// of some (potentially dead) allocation.
255-
if ptr.offset > size {
256-
return err!(PointerOutOfBounds {
257-
ptr: ptr.erase_tag(),
258-
access: true,
259-
allocation_size: size,
260-
});
261-
};
262-
// keep data for alignment check
254+
self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead)?;
255+
// data required for alignment check
256+
let (_, align) = self.get_size_and_align(ptr.alloc_id);
263257
(ptr.offset.bytes(), align)
264258
}
265259
Scalar::Bits { bits, size } => {
@@ -293,18 +287,28 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
293287

294288
/// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end
295289
/// of an allocation (i.e., at the first *inaccessible* location) *is* considered
296-
/// in-bounds! This follows C's/LLVM's rules. The `access` boolean is just used
297-
/// for the error message.
298-
/// If you want to check bounds before doing a memory access, be sure to
299-
/// check the pointer one past the end of your access, then everything will
300-
/// work out exactly.
301-
pub fn check_bounds_ptr(&self, ptr: Pointer<M::PointerTag>, access: bool) -> EvalResult<'tcx> {
302-
let alloc = self.get(ptr.alloc_id)?;
303-
let allocation_size = alloc.bytes.len() as u64;
290+
/// in-bounds! This follows C's/LLVM's rules. `check` indicates whether we
291+
/// additionally require the pointer to be pointing to a *live* (still allocated)
292+
/// allocation.
293+
/// If you want to check bounds before doing a memory access, better use `check_bounds`.
294+
pub fn check_bounds_ptr(
295+
&self,
296+
ptr: Pointer<M::PointerTag>,
297+
check: InboundsCheck,
298+
) -> EvalResult<'tcx> {
299+
let allocation_size = match check {
300+
InboundsCheck::Live => {
301+
let alloc = self.get(ptr.alloc_id)?;
302+
alloc.bytes.len() as u64
303+
}
304+
InboundsCheck::MaybeDead => {
305+
self.get_size_and_align(ptr.alloc_id).0.bytes()
306+
}
307+
};
304308
if ptr.offset.bytes() > allocation_size {
305309
return err!(PointerOutOfBounds {
306310
ptr: ptr.erase_tag(),
307-
access,
311+
check,
308312
allocation_size: Size::from_bytes(allocation_size),
309313
});
310314
}
@@ -317,10 +321,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
317321
&self,
318322
ptr: Pointer<M::PointerTag>,
319323
size: Size,
320-
access: bool
324+
check: InboundsCheck,
321325
) -> EvalResult<'tcx> {
322326
// if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
323-
self.check_bounds_ptr(ptr.offset(size, &*self)?, access)
327+
self.check_bounds_ptr(ptr.offset(size, &*self)?, check)
324328
}
325329
}
326330

@@ -626,7 +630,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
626630
) -> EvalResult<'tcx, &[u8]> {
627631
assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
628632
self.check_align(ptr.into(), align)?;
629-
self.check_bounds(ptr, size, true)?;
633+
self.check_bounds(ptr, size, InboundsCheck::Live)?;
630634

631635
if check_defined_and_ptr {
632636
self.check_defined(ptr, size)?;
@@ -677,7 +681,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
677681
) -> EvalResult<'tcx, &mut [u8]> {
678682
assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
679683
self.check_align(ptr.into(), align)?;
680-
self.check_bounds(ptr, size, true)?;
684+
self.check_bounds(ptr, size, InboundsCheck::Live)?;
681685

682686
self.mark_definedness(ptr, size, true)?;
683687
self.clear_relocations(ptr, size)?;

src/librustc_mir/interpret/operand.rs

+17-12
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerEx
1919
use rustc::mir::interpret::{
2020
GlobalId, AllocId,
2121
ConstValue, Pointer, Scalar,
22-
EvalResult, EvalErrorKind
22+
EvalResult, EvalErrorKind, InboundsCheck,
2323
};
2424
use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind};
2525
pub use rustc::mir::interpret::ScalarMaybeUndef;
@@ -601,7 +601,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
601601
// read raw discriminant value
602602
let discr_op = self.operand_field(rval, 0)?;
603603
let discr_val = self.read_immediate(discr_op)?;
604-
let raw_discr = discr_val.to_scalar()?;
604+
let raw_discr = discr_val.to_scalar_or_undef();
605605
trace!("discr value: {:?}", raw_discr);
606606
// post-process
607607
Ok(match rval.layout.variants {
@@ -647,28 +647,33 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
647647
let variants_start = niche_variants.start().as_u32() as u128;
648648
let variants_end = niche_variants.end().as_u32() as u128;
649649
match raw_discr {
650-
Scalar::Ptr(_) => {
651-
// The niche must be just 0 (which a pointer value never is)
652-
assert!(niche_start == 0);
653-
assert!(variants_start == variants_end);
650+
ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) => {
651+
// The niche must be just 0 (which an inbounds pointer value never is)
652+
let ptr_valid = niche_start == 0 && variants_start == variants_end &&
653+
self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead).is_ok();
654+
if !ptr_valid {
655+
return err!(InvalidDiscriminant(raw_discr.erase_tag()));
656+
}
654657
(dataful_variant.as_u32() as u128, dataful_variant)
655658
},
656-
Scalar::Bits { bits: raw_discr, size } => {
659+
ScalarMaybeUndef::Scalar(Scalar::Bits { bits: raw_discr, size }) => {
657660
assert_eq!(size as u64, discr_val.layout.size.bytes());
658-
let discr = raw_discr.wrapping_sub(niche_start)
661+
let adjusted_discr = raw_discr.wrapping_sub(niche_start)
659662
.wrapping_add(variants_start);
660-
if variants_start <= discr && discr <= variants_end {
661-
let index = discr as usize;
662-
assert_eq!(index as u128, discr);
663+
if variants_start <= adjusted_discr && adjusted_discr <= variants_end {
664+
let index = adjusted_discr as usize;
665+
assert_eq!(index as u128, adjusted_discr);
663666
assert!(index < rval.layout.ty
664667
.ty_adt_def()
665668
.expect("tagged layout for non adt")
666669
.variants.len());
667-
(discr, VariantIdx::from_usize(index))
670+
(adjusted_discr, VariantIdx::from_usize(index))
668671
} else {
669672
(dataful_variant.as_u32() as u128, dataful_variant)
670673
}
671674
},
675+
ScalarMaybeUndef::Undef =>
676+
return err!(InvalidDiscriminant(ScalarMaybeUndef::Undef)),
672677
}
673678
}
674679
})

src/librustc_mir/interpret/validity.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc::ty::layout::{self, Size, Align, TyLayout, LayoutOf, VariantIdx};
1717
use rustc::ty;
1818
use rustc_data_structures::fx::FxHashSet;
1919
use rustc::mir::interpret::{
20-
Scalar, AllocType, EvalResult, EvalErrorKind,
20+
Scalar, AllocType, EvalResult, EvalErrorKind, InboundsCheck,
2121
};
2222

2323
use super::{
@@ -274,7 +274,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
274274
),
275275
EvalErrorKind::ReadPointerAsBytes =>
276276
validation_failure!(
277-
"a pointer", self.path, "plain bytes"
277+
"a pointer", self.path, "plain (non-pointer) bytes"
278278
),
279279
_ => Err(err),
280280
}
@@ -304,7 +304,7 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
304304
if self.const_mode {
305305
// Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
306306
try_validation!(value.to_bits(size),
307-
value, self.path, "initialized plain bits");
307+
value, self.path, "initialized plain (non-pointer) bytes");
308308
} else {
309309
// At run-time, for now, we accept *anything* for these types, including
310310
// undef. We should fix that, but let's start low.
@@ -394,7 +394,8 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
394394
}
395395
// Maintain the invariant that the place we are checking is
396396
// already verified to be in-bounds.
397-
try_validation!(self.ecx.memory.check_bounds(ptr, size, false),
397+
try_validation!(
398+
self.ecx.memory.check_bounds(ptr, size, InboundsCheck::Live),
398399
"dangling (not entirely in bounds) reference", self.path);
399400
}
400401
// Check if we have encountered this pointer+layout combination

src/test/ui/consts/const-eval/const-pointer-values-in-various-types.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0080]: it is undefined behavior to use this value
22
--> $DIR/const-pointer-values-in-various-types.rs:24:5
33
|
44
LL | const I32_REF_USIZE_UNION: usize = unsafe { Nonsense { int_32_ref: &3 }.u };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
66
|
77
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
88

@@ -36,7 +36,7 @@ error[E0080]: it is undefined behavior to use this value
3636
--> $DIR/const-pointer-values-in-various-types.rs:36:5
3737
|
3838
LL | const I32_REF_U64_UNION: u64 = unsafe { Nonsense { int_32_ref: &3 }.uint_64 };
39-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
4040
|
4141
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
4242

@@ -74,7 +74,7 @@ error[E0080]: it is undefined behavior to use this value
7474
--> $DIR/const-pointer-values-in-various-types.rs:51:5
7575
|
7676
LL | const I32_REF_I64_UNION: i64 = unsafe { Nonsense { int_32_ref: &3 }.int_64 };
77-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
77+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
7878
|
7979
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
8080

@@ -96,7 +96,7 @@ error[E0080]: it is undefined behavior to use this value
9696
--> $DIR/const-pointer-values-in-various-types.rs:60:5
9797
|
9898
LL | const I32_REF_F64_UNION: f64 = unsafe { Nonsense { int_32_ref: &3 }.float_64 };
99-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
99+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
100100
|
101101
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
102102

@@ -144,7 +144,7 @@ error[E0080]: it is undefined behavior to use this value
144144
--> $DIR/const-pointer-values-in-various-types.rs:78:5
145145
|
146146
LL | const STR_U64_UNION: u64 = unsafe { Nonsense { stringy: "3" }.uint_64 };
147-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
147+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
148148
|
149149
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
150150

@@ -184,7 +184,7 @@ error[E0080]: it is undefined behavior to use this value
184184
--> $DIR/const-pointer-values-in-various-types.rs:93:5
185185
|
186186
LL | const STR_I64_UNION: i64 = unsafe { Nonsense { stringy: "3" }.int_64 };
187-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
187+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
188188
|
189189
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
190190

@@ -208,7 +208,7 @@ error[E0080]: it is undefined behavior to use this value
208208
--> $DIR/const-pointer-values-in-various-types.rs:102:5
209209
|
210210
LL | const STR_F64_UNION: f64 = unsafe { Nonsense { stringy: "3" }.float_64 };
211-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
211+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
212212
|
213213
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
214214

src/test/ui/consts/const-eval/issue-52442.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ error[E0080]: it is undefined behavior to use this value
88
--> $DIR/issue-52442.rs:12:11
99
|
1010
LL | [(); { &loop { break } as *const _ as usize } ]; //~ ERROR unimplemented expression type
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain bits
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a pointer, but expected initialized plain (non-pointer) bytes
1212
|
1313
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rust compiler repository if you believe it should not be considered undefined behavior
1414

0 commit comments

Comments
 (0)