From 684de68d6c2d0d0b4da7cc5cdec92d3fe488ea8c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 5 Jun 2017 18:07:26 -0700 Subject: [PATCH] properly wrap pointer offsets at pointer size --- src/eval_context.rs | 20 ++++++++++---------- src/lvalue.rs | 8 ++++---- src/memory.rs | 24 ++++++++++++++---------- src/terminator/mod.rs | 10 +++++----- src/traits.rs | 10 +++++----- src/value.rs | 4 ++-- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index e601f4b38fac..d45c419c78a9 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -391,7 +391,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); - let discr_dest = dest_ptr.offset(discr_offset)?; + let discr_dest = dest_ptr.offset(discr_offset, self.memory.layout)?; self.memory.write_uint(discr_dest, discr_val, discr_size)?; let dest = Lvalue::Ptr { @@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME(solson) let dest = self.force_allocation(dest)?.to_ptr(); - let dest = dest.offset(offset.bytes())?; + let dest = dest.offset(offset.bytes(), self.memory.layout)?; let dest_size = self.type_size(ty)? .expect("bad StructWrappedNullablePointer discrfield"); self.memory.write_int(dest, 0, dest_size)?; @@ -610,7 +610,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let dest = self.force_allocation(dest)?.to_ptr(); for i in 0..length { - let elem_dest = dest.offset(i * elem_size)?; + let elem_dest = dest.offset(i * elem_size, self.memory.layout)?; self.write_value_to_ptr(value, elem_dest, elem_ty)?; } } @@ -854,7 +854,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; let offset = offset.overflowing_mul(pointee_size).0; - Ok(ptr.wrapping_signed_offset(offset)) + Ok(ptr.wrapping_signed_offset(offset, self.memory.layout)) } pub(super) fn pointer_offset(&self, ptr: Pointer, pointee_ty: Ty<'tcx>, offset: i64) -> EvalResult<'tcx, Pointer> { @@ -865,7 +865,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // FIXME: assuming here that type size is < i64::max_value() let pointee_size = self.type_size(pointee_ty)?.expect("cannot offset a pointer to an unsized type") as i64; return if let Some(offset) = offset.checked_mul(pointee_size) { - let ptr = ptr.signed_offset(offset)?; + let ptr = ptr.signed_offset(offset, self.memory.layout)?; self.memory.check_bounds(ptr, false)?; Ok(ptr) } else { @@ -1124,8 +1124,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let field_1_ty = self.get_field_ty(ty, 1)?; let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized"); let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized"); - self.memory.write_primval(ptr.offset(field_0)?, a, field_0_size)?; - self.memory.write_primval(ptr.offset(field_1)?, b, field_1_size)?; + self.memory.write_primval(ptr.offset(field_0, self.memory.layout)?, a, field_0_size)?; + self.memory.write_primval(ptr.offset(field_1, self.memory.layout)?, b, field_1_size)?; Ok(()) } @@ -1242,7 +1242,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(Value::ByVal(PrimVal::Ptr(p))) } else { trace!("reading fat pointer extra of type {}", pointee_ty); - let extra = ptr.offset(self.memory.pointer_size())?; + let extra = ptr.offset(self.memory.pointer_size(), self.memory.layout)?; let extra = match self.tcx.struct_tail(pointee_ty).sty { ty::TyDynamic(..) => PrimVal::Ptr(self.memory.read_ptr(extra)?), ty::TySlice(..) | @@ -1427,8 +1427,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } let src_field_offset = self.get_field_offset(src_ty, i)?.bytes(); let dst_field_offset = self.get_field_offset(dest_ty, i)?.bytes(); - let src_f_ptr = src_ptr.offset(src_field_offset)?; - let dst_f_ptr = dest.offset(dst_field_offset)?; + let src_f_ptr = src_ptr.offset(src_field_offset, self.memory.layout)?; + let dst_f_ptr = dest.offset(dst_field_offset, self.memory.layout)?; if src_fty == dst_fty { self.copy(src_f_ptr, dst_f_ptr, src_fty)?; } else { diff --git a/src/lvalue.rs b/src/lvalue.rs index e4deddbfb48b..c5811774e941 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -270,7 +270,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => offset.bytes(), }; - let ptr = base_ptr.offset(offset)?; + let ptr = base_ptr.offset(offset, self.memory.layout)?; let field_ty = self.monomorphize(field_ty, self.substs()); @@ -363,7 +363,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let usize = self.tcx.types.usize; let n = self.value_to_primval(n_ptr, usize)?.to_u64()?; assert!(n < len, "Tried to access element {} of array/slice with length {}", n, len); - let ptr = base_ptr.offset(n * elem_size)?; + let ptr = base_ptr.offset(n * elem_size, self.memory.layout)?; (ptr, LvalueExtra::None) } @@ -384,7 +384,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { u64::from(offset) }; - let ptr = base_ptr.offset(index * elem_size)?; + let ptr = base_ptr.offset(index * elem_size, self.memory.layout)?; (ptr, LvalueExtra::None) } @@ -398,7 +398,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (elem_ty, n) = base.elem_ty_and_len(base_ty); let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized"); assert!(u64::from(from) <= n - u64::from(to)); - let ptr = base_ptr.offset(u64::from(from) * elem_size)?; + let ptr = base_ptr.offset(u64::from(from) * elem_size, self.memory.layout)?; let extra = LvalueExtra::Length(n - u64::from(to) - u64::from(from)); (ptr, extra) } diff --git a/src/memory.rs b/src/memory.rs index 32763d47d170..18f757a6bd6c 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -60,11 +60,11 @@ impl Pointer { Pointer { alloc_id, offset } } - pub fn wrapping_signed_offset<'tcx>(self, i: i64) -> Self { - Pointer::new(self.alloc_id, self.offset.wrapping_add(i as u64)) + pub fn wrapping_signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> Self { + Pointer::new(self.alloc_id, (self.offset.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64) } - pub fn signed_offset<'tcx>(self, i: i64) -> EvalResult<'tcx, Self> { + pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { // FIXME: is it possible to over/underflow here? if i < 0 { // trickery to ensure that i64::min_value() works fine @@ -76,13 +76,17 @@ impl Pointer { Err(EvalError::OverflowingPointerMath) } } else { - self.offset(i as u64) + self.offset(i as u64, layout) } } - pub fn offset<'tcx>(self, i: u64) -> EvalResult<'tcx, Self> { + pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { if let Some(res) = self.offset.checked_add(i) { - Ok(Pointer::new(self.alloc_id, res)) + if res as u128 >= (1u128 << layout.pointer_size.bits()) { + Err(EvalError::OverflowingPointerMath) + } else { + Ok(Pointer::new(self.alloc_id, res)) + } } else { Err(EvalError::OverflowingPointerMath) } @@ -283,7 +287,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { alloc.undef_mask.grow(amount, false); } else if size > new_size { self.memory_usage -= size - new_size; - self.clear_relocations(ptr.offset(new_size)?, size - new_size)?; + self.clear_relocations(ptr.offset(new_size, self.layout)?, size - new_size)?; let alloc = self.get_mut(ptr.alloc_id)?; // `as usize` is fine here, since it is smaller than `size`, which came from a usize alloc.bytes.truncate(new_size as usize); @@ -595,7 +599,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&[]); } self.check_align(ptr, align, size)?; - self.check_bounds(ptr.offset(size)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -608,7 +612,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { return Ok(&mut []); } self.check_align(ptr, align, size)?; - self.check_bounds(ptr.offset(size)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) + self.check_bounds(ptr.offset(size, self.layout)?, true)?; // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow) let alloc = self.get_mut(ptr.alloc_id)?; assert_eq!(ptr.offset as usize as u64, ptr.offset); assert_eq!(size as usize as u64, size); @@ -930,7 +934,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { fn check_relocation_edges(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx> { let overlapping_start = self.relocations(ptr, 0)?.count(); - let overlapping_end = self.relocations(ptr.offset(size)?, 0)?.count(); + let overlapping_end = self.relocations(ptr.offset(size, self.layout)?, 0)?.count(); if overlapping_start + overlapping_end != 0 { return Err(EvalError::ReadPointerAsBytes); } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index ebf8723e827e..afc3bf1c37fe 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -305,7 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match arg_val { Value::ByRef(ptr) => { for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) { - let arg = Value::ByRef(ptr.offset(offset)?); + let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?); let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty); self.write_value(arg, dest, ty)?; @@ -387,7 +387,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::InstanceDef::Virtual(_, idx) => { let ptr_size = self.memory.pointer_size(); let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?; - let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3))?)?; + let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?; let instance = self.memory.get_fn(fn_ptr.alloc_id)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); @@ -473,7 +473,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => { let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?; - let nonnull = adt_ptr.offset(offset.bytes())?; + let nonnull = adt_ptr.offset(offset.bytes(), self.memory.layout)?; trace!("struct wrapped nullable pointer type: {}", ty); // only the pointer part of a fat pointer is used for this space optimization let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield"); @@ -654,7 +654,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) { - let new_ptr = ptr.offset(num - idx as u64 - 1)?; + let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; @@ -666,7 +666,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8; let num = self.value_to_primval(args[2], usize)?.to_u64()?; if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) { - let new_ptr = ptr.offset(idx as u64)?; + let new_ptr = ptr.offset(idx as u64, self.memory.layout)?; self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; } else { self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; diff --git a/src/traits.rs b/src/traits.rs index bf9e47da991a..322ebc1981b4 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -56,14 +56,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let drop = self.memory.create_fn_alloc(drop); self.memory.write_ptr(vtable, drop)?; - self.memory.write_usize(vtable.offset(ptr_size)?, size)?; - self.memory.write_usize(vtable.offset(ptr_size * 2)?, align)?; + self.memory.write_usize(vtable.offset(ptr_size, self.memory.layout)?, size)?; + self.memory.write_usize(vtable.offset(ptr_size * 2, self.memory.layout)?, align)?; for (i, method) in ::rustc::traits::get_vtable_methods(self.tcx, trait_ref).enumerate() { if let Some((def_id, substs)) = method { let instance = ::eval_context::resolve(self.tcx, def_id, substs); let fn_ptr = self.memory.create_fn_alloc(instance); - self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64))?, fn_ptr)?; + self.memory.write_ptr(vtable.offset(ptr_size * (3 + i as u64), self.memory.layout)?, fn_ptr)?; } } @@ -88,8 +88,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn read_size_and_align_from_vtable(&self, vtable: Pointer) -> EvalResult<'tcx, (u64, u64)> { let pointer_size = self.memory.pointer_size(); - let size = self.memory.read_usize(vtable.offset(pointer_size)?)?; - let align = self.memory.read_usize(vtable.offset(pointer_size * 2)?)?; + let size = self.memory.read_usize(vtable.offset(pointer_size, self.memory.layout)?)?; + let align = self.memory.read_usize(vtable.offset(pointer_size * 2, self.memory.layout)?)?; Ok((size, align)) } diff --git a/src/value.rs b/src/value.rs index fe4c6608ed38..387002eee7bc 100644 --- a/src/value.rs +++ b/src/value.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size())?)?; + let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; Ok((ptr, vtable)) } @@ -105,7 +105,7 @@ impl<'a, 'tcx: 'a> Value { match *self { ByRef(ref_ptr) => { let ptr = mem.read_ptr(ref_ptr)?; - let len = mem.read_usize(ref_ptr.offset(mem.pointer_size())?)?; + let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?)?; Ok((ptr, len)) }, ByValPair(ptr, val) => {