Skip to content

Commit

Permalink
Merge pull request rust-lang#342 from RalfJung/mir-validate
Browse files Browse the repository at this point in the history
validation: check that int, float etc. are not undef
  • Loading branch information
oli-obk authored Sep 16, 2017
2 parents 1fc3a00 + bc240ff commit 6d4840e
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
Value::ByRef { .. } => bug!("follow_by_ref_value can't result in `ByRef`"),

Value::ByVal(primval) => {
// TODO: Do we really want insta-UB here?
self.ensure_valid_value(primval, ty)?;
Ok(primval)
}
Expand Down Expand Up @@ -1817,6 +1818,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let val = match val {
PrimVal::Bytes(0) => false,
PrimVal::Bytes(1) => true,
// TODO: This seems a little overeager, should reading at bool type already be UB?
_ => return err!(InvalidBool),
};
PrimVal::from_bool(val)
Expand Down
22 changes: 18 additions & 4 deletions src/librustc_mir/interpret/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,12 +492,22 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
let res = do catch {
match query.ty.sty {
TyInt(_) | TyUint(_) | TyRawPtr(_) => {
// TODO: Make sure these are not undef.
// We could do a bounds-check and other sanity checks on the lvalue, but it would be a bug in miri for this to ever fail.
if mode.acquiring() {
// Make sure we can read this.
let val = self.read_lvalue(query.lval.1)?;
self.follow_by_ref_value(val, query.ty)?;
// FIXME: It would be great to rule out Undef here, but that doesn't actually work.
// Passing around undef data is a thing that e.g. Vec::extend_with does.
}
Ok(())
}
TyBool | TyFloat(_) | TyChar | TyStr => {
// TODO: Check if these are valid bool/float/codepoint/UTF-8, respectively (and in particular, not undef).
TyBool | TyFloat(_) | TyChar => {
if mode.acquiring() {
let val = self.read_lvalue(query.lval.1)?;
let val = self.value_to_primval(ValTy { value: val, ty: query.ty })?;
val.to_bytes()?;
// TODO: Check if these are valid bool/float/codepoint/UTF-8
}
Ok(())
}
TyNever => err!(ValidationFailure(format!("The empty type is never valid."))),
Expand Down Expand Up @@ -542,6 +552,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}

// Compound types
TyStr => {
// TODO: Validate strings
Ok(())
}
TySlice(elem_ty) => {
let len = match query.lval.1 {
Lvalue::Ptr { extra: LvalueExtra::Length(len), .. } => len,
Expand Down
3 changes: 3 additions & 0 deletions tests/compile-fail-fullmir/undefined_byte_read.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// This should fail even without validation
// compile-flags: -Zmir-emit-validate=0

fn main() {
let v: Vec<u8> = Vec::with_capacity(10);
let undef = unsafe { *v.get_unchecked(5) };
Expand Down
4 changes: 2 additions & 2 deletions tests/compile-fail/invalid_bool.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
fn main() {
let b = unsafe { std::mem::transmute::<u8, bool>(2) };
if b { unreachable!() } else { unreachable!() } //~ ERROR: invalid boolean value read
let b = unsafe { std::mem::transmute::<u8, bool>(2) }; //~ ERROR: invalid boolean value read
if b { unreachable!() } else { unreachable!() }
}
4 changes: 2 additions & 2 deletions tests/compile-fail/match_char.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
fn main() {
assert!(std::char::from_u32(-1_i32 as u32).is_none());
match unsafe { std::mem::transmute::<i32, char>(-1) } {
'a' => {}, //~ERROR tried to interpret an invalid 32-bit value as a char: 4294967295
match unsafe { std::mem::transmute::<i32, char>(-1) } { //~ERROR tried to interpret an invalid 32-bit value as a char: 4294967295
'a' => {},
'b' => {},
_ => {},
}
Expand Down
3 changes: 3 additions & 0 deletions tests/compile-fail/reference_to_packed.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// This should fail even without validation
// compile-flags: -Zmir-emit-validate=0

#![allow(dead_code, unused_variables)]

#[repr(packed)]
Expand Down
2 changes: 2 additions & 0 deletions tests/compile-fail/transmute_fat.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// This should fail even without validation
// compile-flags: -Zmir-emit-validate=0
#![feature(i128_type)]

fn main() {
Expand Down
14 changes: 14 additions & 0 deletions tests/compile-fail/validation_undef.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#![allow(unused_variables)]
// error-pattern: attempted to read undefined bytes

mod safe {
use std::mem;

pub(crate) fn make_float() -> f32 {
unsafe { mem::uninitialized() }
}
}

fn main() {
let _x = safe::make_float();
}
3 changes: 3 additions & 0 deletions tests/run-pass/move-undef-primval.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Moving around undef is not allowed by validation
// compile-flags: -Zmir-emit-validate=0

struct Foo {
_inner: i32,
}
Expand Down

0 comments on commit 6d4840e

Please sign in to comment.