Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

miri: detect too large dynamically sized objects #64014

Merged
merged 5 commits into from
Aug 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,27 +442,30 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

// Issue #27023: must add any necessary padding to `size`
// (to make it a multiple of `align`) before returning it.
//
// Namely, the returned size should be, in C notation:
//
// `size + ((size & (align-1)) ? align : 0)`
//
// emulated via the semi-standard fast bit trick:
//
// `(size + (align-1)) & -align`

Ok(Some((size.align_to(align), align)))
let size = size.align_to(align);

// Check if this brought us over the size limit.
if size.bytes() >= self.tcx.data_layout().obj_size_bound() {
throw_ub_format!("wide pointer metadata contains invalid information: \
total size is bigger than largest supported object");
}
Ok(Some((size, align)))
}
ty::Dynamic(..) => {
let vtable = metadata.expect("dyn trait fat ptr must have vtable");
// the second entry in the vtable is the dynamic size of the object.
// Read size and align from vtable (already checks size).
Ok(Some(self.read_size_and_align_from_vtable(vtable)?))
}

ty::Slice(_) | ty::Str => {
let len = metadata.expect("slice fat ptr must have vtable").to_usize(self)?;
let elem = layout.field(self, 0)?;
Ok(Some((elem.size * len, elem.align.abi)))

// Make sure the slice is not too big.
let size = elem.size.checked_mul(len, &*self.tcx)
.ok_or_else(|| err_ub_format!("invalid slice: \
total size is bigger than largest supported object"))?;
Ok(Some((size, elem.align.abi)))
}

ty::Foreign(_) => {
Expand Down
7 changes: 6 additions & 1 deletion src/librustc_mir/interpret/traits.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc::ty::{self, Ty, Instance, TypeFoldable};
use rustc::ty::layout::{Size, Align, LayoutOf};
use rustc::ty::layout::{Size, Align, LayoutOf, HasDataLayout};
use rustc::mir::interpret::{Scalar, Pointer, InterpResult, PointerArithmetic,};

use super::{InterpCx, Machine, MemoryKind, FnVal};
Expand Down Expand Up @@ -151,6 +151,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
vtable.offset(pointer_size * 2, self)?,
)?.not_undef()?;
let align = self.force_bits(align, pointer_size)? as u64;

if size >= self.tcx.data_layout().obj_size_bound() {
throw_ub_format!("invalid vtable: \
size is bigger than largest supported object");
}
Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap()))
}
}
6 changes: 6 additions & 0 deletions src/librustc_mir/interpret/validity.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! Check the validity invariant of a given value, and tell the user
//! where in the value it got violated.
//! In const context, this goes even further and tries to approximate const safety.
//! That's useful because it means other passes (e.g. promotion) can rely on `const`s
//! to be const-safe.

use std::fmt::Write;
use std::ops::RangeInclusive;

Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/consts/const-eval/dangling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(const_transmute, const_raw_ptr_deref)]

use std::{mem, usize};

// Make sure we error with the right kind of error on a too large slice.
const TEST: () = { unsafe { //~ NOTE
let slice: *const [u8] = mem::transmute((1usize, usize::MAX));
let _val = &*slice; //~ ERROR: any use of this value will cause an error
//~^ NOTE: total size is bigger than largest supported object
//~^^ on by default
} };

fn main() {}
16 changes: 16 additions & 0 deletions src/test/ui/consts/const-eval/dangling.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: any use of this value will cause an error
--> $DIR/dangling.rs:8:16
|
LL | / const TEST: () = { unsafe {
LL | | let slice: *const [u8] = mem::transmute((1usize, usize::MAX));
LL | | let _val = &*slice;
| | ^^^^^^^ invalid slice: total size is bigger than largest supported object
LL | |
LL | |
LL | | } };
| |____-
|
= note: `#[deny(const_err)]` on by default

error: aborting due to previous error

23 changes: 13 additions & 10 deletions src/test/ui/consts/const-eval/ub-wide-ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// normalize-stderr-test "allocation \d+" -> "allocation N"
// normalize-stderr-test "size \d+" -> "size N"

#[repr(C)]
union BoolTransmute {
val: u8,
bl: bool,
Expand All @@ -26,6 +27,7 @@ struct BadSliceRepr {
len: &'static u8,
}

#[repr(C)]
union SliceTransmute {
repr: SliceRepr,
bad: BadSliceRepr,
Expand Down Expand Up @@ -58,6 +60,7 @@ struct BadDynRepr {
vtable: usize,
}

#[repr(C)]
union DynTransmute {
repr: DynRepr,
repr2: DynRepr2,
Expand Down Expand Up @@ -91,10 +94,10 @@ const MY_STR_LENGTH_PTR: &MyStr = unsafe { SliceTransmute { bad: BadSliceRepr {
//~^ ERROR it is undefined behavior to use this value

// invalid UTF-8
const J1: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
const STR_NO_UTF8: &str = unsafe { SliceTransmute { slice: &[0xFF] }.str };
//~^ ERROR it is undefined behavior to use this value
// invalid UTF-8 in user-defined str-like
const J2: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str };
const MYSTR_NO_UTF8: &MyStr = unsafe { SliceTransmute { slice: &[0xFF] }.my_str };
//~^ ERROR it is undefined behavior to use this value

// # slice
Expand All @@ -111,16 +114,16 @@ const SLICE_LENGTH_PTR: &[u8] = unsafe { SliceTransmute { bad: BadSliceRepr { pt
//~^ ERROR it is undefined behavior to use this value

// bad data *inside* the slice
const H: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
const SLICE_CONTENT_INVALID: &[bool] = &[unsafe { BoolTransmute { val: 3 }.bl }];
//~^ ERROR it is undefined behavior to use this value

// good MySliceBool
const I1: &MySliceBool = &MySlice(true, [false]);
const MYSLICE_GOOD: &MySliceBool = &MySlice(true, [false]);
// bad: sized field is not okay
const I2: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]);
const MYSLICE_PREFIX_BAD: &MySliceBool = &MySlice(unsafe { BoolTransmute { val: 3 }.bl }, [false]);
//~^ ERROR it is undefined behavior to use this value
// bad: unsized part is not okay
const I3: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]);
const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { BoolTransmute { val: 3 }.bl }]);
//~^ ERROR it is undefined behavior to use this value

// # raw slice
Expand All @@ -132,17 +135,17 @@ const RAW_SLICE_LENGTH_UNINIT: *const [u8] = unsafe { SliceTransmute { addr: 42

// # trait object
// bad trait object
const D: &dyn Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
const TRAIT_OBJ_SHORT_VTABLE_1: &dyn Trait = unsafe { DynTransmute { repr: DynRepr { ptr: &92, vtable: &3 } }.rust};
//~^ ERROR it is undefined behavior to use this value
// bad trait object
const E: &dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
const TRAIT_OBJ_SHORT_VTABLE_2: &dyn Trait = unsafe { DynTransmute { repr2: DynRepr2 { ptr: &92, vtable: &3 } }.rust};
//~^ ERROR it is undefined behavior to use this value
// bad trait object
const F: &dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
const TRAIT_OBJ_INT_VTABLE: &dyn Trait = unsafe { DynTransmute { bad: BadDynRepr { ptr: &92, vtable: 3 } }.rust};
//~^ ERROR it is undefined behavior to use this value

// bad data *inside* the trait object
const G: &dyn Trait = &unsafe { BoolTransmute { val: 3 }.bl };
const TRAIT_OBJ_CONTENT_INVALID: &dyn Trait = &unsafe { BoolTransmute { val: 3 }.bl };
//~^ ERROR it is undefined behavior to use this value

// # raw trait object
Expand Down
Loading