Skip to content

Commit ff5a245

Browse files
committed
check that entire ref is in-bounds before recursing; add macro for validation msgs on error
1 parent bf5e6eb commit ff5a245

File tree

7 files changed

+86
-96
lines changed

7 files changed

+86
-96
lines changed

src/librustc_mir/interpret/memory.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
284284
/// If you want to check bounds before doing a memory access, be sure to
285285
/// check the pointer one past the end of your access, then everything will
286286
/// work out exactly.
287-
pub fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> {
287+
pub fn check_bounds_ptr(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> {
288288
let alloc = self.get(ptr.alloc_id)?;
289289
let allocation_size = alloc.bytes.len() as u64;
290290
if ptr.offset.bytes() > allocation_size {
@@ -296,6 +296,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
296296
}
297297
Ok(())
298298
}
299+
300+
/// Check if the memory range beginning at `ptr` and of size `Size` is "in-bounds".
301+
#[inline(always)]
302+
pub fn check_bounds(&self, ptr: Pointer, size: Size, access: bool) -> EvalResult<'tcx> {
303+
// if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
304+
self.check_bounds_ptr(ptr.offset(size, &*self)?, access)
305+
}
299306
}
300307

301308
/// Allocation accessors
@@ -524,8 +531,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
524531
) -> EvalResult<'tcx, &[u8]> {
525532
assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
526533
self.check_align(ptr.into(), align)?;
527-
// if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
528-
self.check_bounds(ptr.offset(size, &*self)?, true)?;
534+
self.check_bounds(ptr, size, true)?;
529535

530536
if check_defined_and_ptr {
531537
self.check_defined(ptr, size)?;
@@ -569,8 +575,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
569575
) -> EvalResult<'tcx, &mut [u8]> {
570576
assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
571577
self.check_align(ptr.into(), align)?;
572-
// if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
573-
self.check_bounds(ptr.offset(size, &self)?, true)?;
578+
self.check_bounds(ptr, size, true)?;
574579

575580
self.mark_definedness(ptr, size, true)?;
576581
self.clear_relocations(ptr, size)?;

src/librustc_mir/interpret/validity.rs

Lines changed: 40 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use super::{
2222
OpTy, MPlaceTy, Machine, EvalContext, ScalarMaybeUndef
2323
};
2424

25-
macro_rules! validation_failure{
25+
macro_rules! validation_failure {
2626
($what:expr, $where:expr, $details:expr) => {{
2727
let where_ = path_format($where);
2828
let where_ = if where_.is_empty() {
@@ -49,6 +49,15 @@ macro_rules! validation_failure{
4949
}};
5050
}
5151

52+
macro_rules! try_validation {
53+
($e:expr, $what:expr, $where:expr) => {{
54+
match $e {
55+
Ok(x) => x,
56+
Err(_) => return validation_failure!($what, $where),
57+
}
58+
}}
59+
}
60+
5261
/// We want to show a nice path to the invalid field for diagnotsics,
5362
/// but avoid string operations in the happy case where no error happens.
5463
/// So we track a `Vec<PathElem>` where `PathElem` contains all the data we
@@ -230,8 +239,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
230239
path: &mut Vec<PathElem>,
231240
ref_tracking: Option<&mut RefTracking<'tcx>>,
232241
) -> EvalResult<'tcx> {
233-
// Skip recursion for some external statics
234-
if let Scalar::Ptr(ptr) = place.ptr {
242+
// Before we do anything else, make sure this is entirely in-bounds.
243+
if !place.layout.is_zst() {
244+
let ptr = try_validation!(place.ptr.to_ptr(),
245+
"integer pointer in non-ZST reference", path);
246+
let size = self.size_and_align_of(place.extra, place.layout)?.0;
247+
try_validation!(self.memory.check_bounds(ptr, size, false),
248+
"dangling reference (not entirely in bounds)", path);
249+
// Skip recursion for some external statics
235250
let alloc_kind = self.tcx.alloc_map.lock().get(ptr.alloc_id);
236251
if let Some(AllocType::Static(did)) = alloc_kind {
237252
// statics from other crates are already checked.
@@ -257,7 +272,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
257272
Ok(())
258273
}
259274

260-
/// This function checks the data at `op`.
275+
/// This function checks the data at `op`. `op` is assumed to cover valid memory if it
276+
/// is an indirect operand.
261277
/// It will error if the bits at the destination do not match the ones described by the layout.
262278
/// The `path` may be pushed to, but the part that is present when the function
263279
/// starts must not be changed!
@@ -305,13 +321,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
305321
let dest = match dest.layout.ty.sty {
306322
ty::Dynamic(..) => {
307323
let dest = dest.to_mem_place(); // immediate trait objects are not a thing
308-
match self.unpack_dyn_trait(dest) {
309-
Ok(res) => res.1.into(),
310-
Err(_) =>
311-
return validation_failure!(
312-
"invalid vtable in fat pointer", path
313-
),
314-
}
324+
try_validation!(self.unpack_dyn_trait(dest),
325+
"invalid vtable in fat pointer", path).1.into()
315326
}
316327
_ => dest
317328
};
@@ -337,20 +348,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
337348
// expectation.
338349
layout::Abi::Scalar(ref scalar_layout) => {
339350
let size = scalar_layout.value.size(self);
340-
let value = match self.read_value(dest) {
341-
Ok(val) => val,
342-
Err(err) => match err.kind {
343-
EvalErrorKind::PointerOutOfBounds { .. } |
344-
EvalErrorKind::ReadUndefBytes(_) =>
345-
return validation_failure!(
346-
"uninitialized or out-of-bounds memory", path
347-
),
348-
_ =>
349-
return validation_failure!(
350-
"unrepresentable data", path
351-
),
352-
}
353-
};
351+
let value = try_validation!(self.read_value(dest),
352+
"uninitialized or unrepresentable data", path);
354353
let scalar = value.to_scalar_or_undef();
355354
self.validate_scalar(scalar, size, scalar_layout, &path, dest.layout.ty)?;
356355
// Recursively check *safe* references
@@ -367,35 +366,24 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
367366
if dest.layout.ty.builtin_deref(true).is_some() =>
368367
{
369368
// This is a fat pointer.
370-
let ptr = match self.read_value(dest.into())
371-
.and_then(|val| self.ref_to_mplace(val))
372-
{
373-
Ok(ptr) => ptr,
374-
Err(_) =>
375-
return validation_failure!(
376-
"undefined location or metadata in fat pointer", path
377-
),
378-
};
369+
let ptr = try_validation!(self.read_value(dest.into()),
370+
"undefined location in fat pointer", path);
371+
let ptr = try_validation!(self.ref_to_mplace(ptr),
372+
"undefined metadata in fat pointer", path);
379373
// check metadata early, for better diagnostics
380374
match self.tcx.struct_tail(ptr.layout.ty).sty {
381375
ty::Dynamic(..) => {
382-
match ptr.extra.unwrap().to_ptr() {
383-
Ok(_) => {},
384-
Err(_) =>
385-
return validation_failure!(
386-
"non-pointer vtable in fat pointer", path
387-
),
388-
}
376+
let vtable = try_validation!(ptr.extra.unwrap().to_ptr(),
377+
"non-pointer vtable in fat pointer", path);
378+
try_validation!(self.read_drop_type_from_vtable(vtable),
379+
"invalid drop fn in vtable", path);
380+
try_validation!(self.read_size_and_align_from_vtable(vtable),
381+
"invalid size or align in vtable", path);
389382
// FIXME: More checks for the vtable.
390383
}
391384
ty::Slice(..) | ty::Str => {
392-
match ptr.extra.unwrap().to_usize(self) {
393-
Ok(_) => {},
394-
Err(_) =>
395-
return validation_failure!(
396-
"non-integer slice length in fat pointer", path
397-
),
398-
}
385+
try_validation!(ptr.extra.unwrap().to_usize(self),
386+
"non-integer slice length in fat pointer", path);
399387
}
400388
_ =>
401389
bug!("Unexpected unsized type tail: {:?}",
@@ -418,23 +406,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
418406
match dest.layout.ty.sty {
419407
// Special handling for strings to verify UTF-8
420408
ty::Str => {
421-
match self.read_str(dest) {
422-
Ok(_) => {},
423-
Err(err) => match err.kind {
424-
EvalErrorKind::PointerOutOfBounds { .. } |
425-
EvalErrorKind::ReadUndefBytes(_) =>
426-
// The error here looks slightly different than it does
427-
// for slices, because we do not report the index into the
428-
// str at which we are OOB.
429-
return validation_failure!(
430-
"uninitialized or out-of-bounds memory", path
431-
),
432-
_ =>
433-
return validation_failure!(
434-
"non-UTF-8 data in str", path
435-
),
436-
}
437-
}
409+
try_validation!(self.read_str(dest),
410+
"uninitialized or non-UTF-8 data in str", path);
438411
}
439412
// Special handling for arrays/slices of builtin integer types
440413
ty::Array(tys, ..) | ty::Slice(tys) if {
@@ -470,18 +443,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
470443
"undefined bytes", path
471444
)
472445
},
473-
EvalErrorKind::PointerOutOfBounds { allocation_size, .. } => {
474-
// If the array access is out-of-bounds, the first
475-
// undefined access is the after the end of the array.
476-
let i = (allocation_size.bytes() * ty_size) as usize;
477-
path.push(PathElem::ArrayElem(i));
478-
},
479-
_ => (),
446+
// Other errors shouldn't be possible
447+
_ => return Err(err),
480448
}
481-
482-
return validation_failure!(
483-
"uninitialized or out-of-bounds memory", path
484-
)
485449
}
486450
}
487451
},

src/test/ui/consts/const-eval/ub-uninhabit.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99
// except according to those terms.
1010

1111
union Foo {
12-
a: u8,
12+
a: usize,
1313
b: Bar,
14+
c: &'static Bar,
1415
}
1516

1617
#[derive(Copy, Clone)]
1718
enum Bar {}
1819

19-
const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b};
20+
const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b };
21+
//~^ ERROR this constant likely exhibits undefined behavior
22+
23+
const BAD_BAD_REF: &Bar = unsafe { Foo { a: 1 }.c };
2024
//~^ ERROR this constant likely exhibits undefined behavior
2125

2226
fn main() {
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
error[E0080]: this constant likely exhibits undefined behavior
2-
--> $DIR/ub-uninhabit.rs:19:1
2+
--> $DIR/ub-uninhabit.rs:20:1
33
|
4-
LL | const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b};
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
4+
LL | const BAD_BAD_BAD: Bar = unsafe { Foo { a: 1 }.b };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type
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

9-
error: aborting due to previous error
9+
error[E0080]: this constant likely exhibits undefined behavior
10+
--> $DIR/ub-uninhabit.rs:23:1
11+
|
12+
LL | const BAD_BAD_REF: &Bar = unsafe { Foo { a: 1 }.c };
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered a value of an uninhabited type at .<deref>
14+
|
15+
= 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
16+
17+
error: aborting due to 2 previous errors
1018

1119
For more information about this error, try `rustc --explain E0080`.

src/test/ui/consts/const-eval/ub-usize-in-ref.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
// compile-pass
12-
1311
union Foo {
1412
a: &'static u8,
1513
b: usize,
1614
}
1715

18-
// This might point to an invalid address, but that's the user's problem
1916
const USIZE_AS_STATIC_REF: &'static u8 = unsafe { Foo { b: 1337 }.a};
17+
//~^ ERROR this constant likely exhibits undefined behavior
2018

2119
fn main() {
2220
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0080]: this constant likely exhibits undefined behavior
2+
--> $DIR/ub-usize-in-ref.rs:16:1
3+
|
4+
LL | const USIZE_AS_STATIC_REF: &'static u8 = unsafe { Foo { b: 1337 }.a};
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered integer pointer in non-ZST reference
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 rust compiler repository if you believe it should not be considered undefined behavior
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0080`.

src/test/ui/union-ub-fat-ptr.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0080]: this constant likely exhibits undefined behavior
22
--> $DIR/union-ub-fat-ptr.rs:87:1
33
|
44
LL | const B: &str = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.str};
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .<deref>
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling reference (not entirely in bounds)
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

@@ -26,7 +26,7 @@ error[E0080]: this constant likely exhibits undefined behavior
2626
--> $DIR/union-ub-fat-ptr.rs:99:1
2727
|
2828
LL | const B2: &[u8] = unsafe { SliceTransmute { repr: SliceRepr { ptr: &42, len: 999 } }.slice};
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or out-of-bounds memory at .<deref>[1]
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered dangling reference (not entirely in bounds)
3030
|
3131
= 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
3232

@@ -42,15 +42,15 @@ error[E0080]: this constant likely exhibits undefined behavior
4242
--> $DIR/union-ub-fat-ptr.rs:106:1
4343
|
4444
LL | const D: &Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
45-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at .<deref>
45+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in vtable
4646
|
4747
= 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
4848

4949
error[E0080]: this constant likely exhibits undefined behavior
5050
--> $DIR/union-ub-fat-ptr.rs:109:1
5151
|
5252
LL | const E: &Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
53-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid vtable in fat pointer at .<deref>
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered invalid drop fn in vtable
5454
|
5555
= 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
5656

@@ -98,15 +98,15 @@ error[E0080]: this constant likely exhibits undefined behavior
9898
--> $DIR/union-ub-fat-ptr.rs:132:1
9999
|
100100
LL | const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
101-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at .<deref>
101+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at .<deref>
102102
|
103103
= 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
104104

105105
error[E0080]: this constant likely exhibits undefined behavior
106106
--> $DIR/union-ub-fat-ptr.rs:135:1
107107
|
108108
LL | const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str };
109-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered non-UTF-8 data in str at .<deref>.0
109+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered uninitialized or non-UTF-8 data in str at .<deref>.0
110110
|
111111
= 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
112112

0 commit comments

Comments
 (0)