about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2019-05-26 14:12:54 +0200
committerRalf Jung <post@ralfj.de>2019-05-26 14:12:54 +0200
commit3defb3f18fdd6d2bd74b3de7a2d932133c99303b (patch)
tree2349961a5917ced4dff683642cd603ad832c2070
parent082da0c6984dd37ab5d65db939c538f0d24bc19f (diff)
downloadrust-3defb3f18fdd6d2bd74b3de7a2d932133c99303b.tar.gz
rust-3defb3f18fdd6d2bd74b3de7a2d932133c99303b.zip
fix overflow error in signed wrapping offset
-rw-r--r--src/librustc/mir/interpret/pointer.rs45
1 files changed, 24 insertions, 21 deletions
diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs
index 356c4cc16c2..33c85a6f46e 100644
--- a/src/librustc/mir/interpret/pointer.rs
+++ b/src/librustc/mir/interpret/pointer.rs
@@ -20,30 +20,20 @@ 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)
-    }
-
-    #[inline]
-    fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
-        let (res, over) = self.overflowing_offset(val, i);
-        if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
+        ((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, 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) }
+        let res = val.overflowing_add(i);
+        self.truncate_to_ptr(res)
     }
 
     // Overflow checking only works properly on the range from -u64 to +u64.
@@ -51,14 +41,27 @@ pub trait PointerArithmetic: layout::HasDataLayout {
     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!
+            // 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;
-            val.overflowing_sub(n)
+            let res = val.overflowing_sub(n);
+            self.truncate_to_ptr(res)
         } else {
             self.overflowing_offset(val, i as u64)
         }
     }
+
+    #[inline]
+    fn offset<'tcx>(&self, val: u64, i: u64) -> EvalResult<'tcx, u64> {
+        let (res, over) = self.overflowing_offset(val, i);
+        if over { err!(Overflow(mir::BinOp::Add)) } else { Ok(res) }
+    }
+
+    #[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) }
+    }
 }
 
 impl<T: layout::HasDataLayout> PointerArithmetic for T {}