Error out when "primitive MIR math" (as opposed to unchecked intrinsics) overflows

Fixes #178
This commit is contained in:
Ralf Jung 2017-06-07 15:39:44 -07:00
parent e1562fbe71
commit 3e1596d8c9
5 changed files with 17 additions and 11 deletions

View File

@ -23,7 +23,6 @@ pub enum EvalError<'tcx> {
},
ReadPointerAsBytes,
InvalidPointerMath,
OverflowingPointerMath,
ReadUndefBytes,
DeadLocal,
InvalidBoolOp(mir::BinOp),
@ -32,6 +31,7 @@ pub enum EvalError<'tcx> {
ExecuteMemory,
ArrayIndexOutOfBounds(Span, u64, u64),
Math(Span, ConstMathErr),
OverflowingMath,
InvalidChar(u128),
OutOfMemory {
allocation_size: u64,
@ -83,8 +83,6 @@ impl<'tcx> Error for EvalError<'tcx> {
"a raw memory access tried to access part of a pointer value as raw bytes",
EvalError::InvalidPointerMath =>
"attempted to do math or a comparison on pointers into different allocations",
EvalError::OverflowingPointerMath =>
"attempted to do overflowing math on a pointer",
EvalError::ReadUndefBytes =>
"attempted to read undefined bytes",
EvalError::DeadLocal =>
@ -100,6 +98,8 @@ impl<'tcx> Error for EvalError<'tcx> {
"array index out of bounds",
EvalError::Math(..) =>
"mathematical operation failed",
EvalError::OverflowingMath =>
"attempted to do overflowing math",
EvalError::NoMirFor(..) =>
"mir not found",
EvalError::InvalidChar(..) =>

View File

@ -452,8 +452,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
BinaryOp(bin_op, ref left, ref right) => {
// ignore overflow bit, rustc inserts check branches for us
self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)?;
if self.intrinsic_overflowing(bin_op, left, right, dest, dest_ty)? {
// There was an overflow in an unchecked binop. Right now, we consider this an error and bail out.
// The rationale is that the reason rustc emits unchecked binops in release mode (vs. the checked binops
// it emits in debug mode) is performance, but it doesn't cust us any performance in miri.
// If, however, the compiler ever starts transforming unchecked intrinsics into unchecked binops,
// we have to go back to just ignoring the overflow here.
return Err(EvalError::OverflowingMath);
}
}
CheckedBinaryOp(bin_op, ref left, ref right) => {
@ -869,7 +875,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.memory.check_bounds(ptr, false)?;
Ok(ptr)
} else {
Err(EvalError::OverflowingPointerMath)
Err(EvalError::OverflowingMath)
}
}

View File

@ -73,7 +73,7 @@ impl Pointer {
if let Some(res) = self.offset.checked_sub(n) {
Ok(Pointer::new(self.alloc_id, res))
} else {
Err(EvalError::OverflowingPointerMath)
Err(EvalError::OverflowingMath)
}
} else {
self.offset(i as u64, layout)
@ -83,12 +83,12 @@ impl Pointer {
pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
if let Some(res) = self.offset.checked_add(i) {
if res as u128 >= (1u128 << layout.pointer_size.bits()) {
Err(EvalError::OverflowingPointerMath)
Err(EvalError::OverflowingMath)
} else {
Ok(Pointer::new(self.alloc_id, res))
}
} else {
Err(EvalError::OverflowingPointerMath)
Err(EvalError::OverflowingMath)
}
}

View File

@ -1,4 +1,4 @@
// error-pattern: overflowing math on a pointer
// error-pattern: overflowing math
fn main() {
let v = [0i8; 4];
let x = &v as *const i8;

View File

@ -1,4 +1,4 @@
//error-pattern: overflowing math on a pointer
//error-pattern: overflowing math
fn main() {
let v = [1i8, 2];
let x = &v[1] as *const i8;