From 3defb3f18fdd6d2bd74b3de7a2d932133c99303b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 26 May 2019 14:12:54 +0200 Subject: [PATCH] fix overflow error in signed wrapping offset --- src/librustc/mir/interpret/pointer.rs | 51 ++++++++++++++------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs index 356c4cc16c23..33c85a6f46e8 100644 --- a/src/librustc/mir/interpret/pointer.rs +++ b/src/librustc/mir/interpret/pointer.rs @@ -20,11 +20,35 @@ pub trait PointerArithmetic: layout::HasDataLayout { self.data_layout().pointer_size } - //// Trunace the given value to the pointer size; also return whether there was an overflow + /// Helper function: truncate given value-"overflowed flag" pair to pointer size and + /// update "overflowed flag" if there was an overflow. + /// This should be called by all the other methods before returning! #[inline] - fn truncate_to_ptr(&self, val: u128) -> (u64, bool) { + fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) { + let val = val as u128; let max_ptr_plus_1 = 1u128 << self.pointer_size().bits(); - ((val % max_ptr_plus_1) as u64, val >= max_ptr_plus_1) + ((val % max_ptr_plus_1) as u64, over || val >= max_ptr_plus_1) + } + + #[inline] + fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { + let res = val.overflowing_add(i); + self.truncate_to_ptr(res) + } + + // Overflow checking only works properly on the range from -u64 to +u64. + #[inline] + fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) { + // FIXME: is it possible to over/underflow here? + if i < 0 { + // Trickery to ensure that i64::min_value() works fine: compute n = -i. + // This formula only works for true negative values, it overflows for zero! + let n = u64::max_value() - (i as u64) + 1; + let res = val.overflowing_sub(n); + self.truncate_to_ptr(res) + } else { + self.overflowing_offset(val, i as u64) + } } #[inline] @@ -33,32 +57,11 @@ pub trait PointerArithmetic: layout::HasDataLayout { if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } } - #[inline] - fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) { - let (res, over1) = val.overflowing_add(i); - let (res, over2) = self.truncate_to_ptr(u128::from(res)); - (res, over1 || over2) - } - #[inline] fn signed_offset<'tcx>(&self, val: u64, i: i64) -> EvalResult<'tcx, u64> { let (res, over) = self.overflowing_signed_offset(val, i128::from(i)); if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) } } - - // Overflow checking only works properly on the range from -u64 to +u64. - #[inline] - fn overflowing_signed_offset(&self, val: u64, i: i128) -> (u64, bool) { - // FIXME: is it possible to over/underflow here? - if i < 0 { - // trickery to ensure that i64::min_value() works fine - // this formula only works for true negative values, it panics for zero! - let n = u64::max_value() - (i as u64) + 1; - val.overflowing_sub(n) - } else { - self.overflowing_offset(val, i as u64) - } - } } impl PointerArithmetic for T {}