From 184d3b3badb9072aee9a439386dcc8e905d1f800 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 19:24:21 -0700 Subject: [PATCH 1/7] expand thread-local storage tests to cover dtor order and re-running dtors --- src/terminator/mod.rs | 2 +- tests/run-pass/thread-local-no-dtor.rs | 18 ------- tests/run-pass/thread-local.rs | 67 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 19 deletions(-) delete mode 100644 tests/run-pass/thread-local-no-dtor.rs create mode 100644 tests/run-pass/thread-local.rs diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index d4e169cbcfb6..636cf2ad0b5e 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -614,7 +614,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; self.write_primval(arg_dest, data, u8_ptr_ty)?; - // We ourselbes return 0 + // We ourselves return 0 self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; // Don't fall through diff --git a/tests/run-pass/thread-local-no-dtor.rs b/tests/run-pass/thread-local-no-dtor.rs deleted file mode 100644 index 4fb43793eaec..000000000000 --- a/tests/run-pass/thread-local-no-dtor.rs +++ /dev/null @@ -1,18 +0,0 @@ -//ignore-windows - -#![feature(libc)] -extern crate libc; - -use std::mem; - -pub type Key = libc::pthread_key_t; - -pub unsafe fn create(dtor: Option) -> Key { - let mut key = 0; - assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); - key -} - -fn main() { - let _ = unsafe { create(None) }; -} diff --git a/tests/run-pass/thread-local.rs b/tests/run-pass/thread-local.rs new file mode 100644 index 000000000000..003fd1ad4c00 --- /dev/null +++ b/tests/run-pass/thread-local.rs @@ -0,0 +1,67 @@ +//ignore-windows + +#![feature(libc)] +extern crate libc; + +use std::mem; + +pub type Key = libc::pthread_key_t; + +static mut RECORD : usize = 0; +static mut KEYS : [Key; 2] = [0; 2]; +static mut GLOBALS : [u64; 2] = [1, 0]; + +static mut CANNARY : *mut u64 = 0 as *mut _; // this serves as a cannary: if TLS dtors are not run properly, this will not get deallocated, making the test fail. + +pub unsafe fn create(dtor: Option) -> Key { + let mut key = 0; + assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); + key +} + +pub unsafe fn set(key: Key, value: *mut u8) { + let r = libc::pthread_setspecific(key, value as *mut _); + assert_eq!(r, 0); +} + +pub fn record(r: usize) { + assert!(r < 10); + unsafe { RECORD = RECORD*10 + r }; +} + +unsafe extern fn dtor(mut ptr: *mut u64) { + assert!(CANNARY != 0 as *mut _); // make sure we do not get run too often + let val = *ptr; + + let which_key = GLOBALS.iter().position(|global| global as *const _ == ptr).expect("Should find my global"); + record(which_key); + + if val > 0 { + *ptr = val-1; + set(KEYS[which_key], ptr as *mut _); + } + + // Check if the records matches what we expect. If yes, clear the cannary. + // If the record is wrong, the cannary will ever get cleared, leading to a leak -> test fails. + // If the record is incomplete (i.e., more dtor calls happen), the check at the beginning of this function will fail -> test fails. + // The correct sequence is: First key 0, then key 1, then key 0. + if RECORD == 0_1_0 { + drop(Box::from_raw(CANNARY)); + CANNARY = 0 as *mut _; + } +} + +fn main() { + unsafe { + create(None); // check that the no-dtor case works + + // Initialize the keys we use to check destructor ordering + for (key, global) in KEYS.iter_mut().zip(GLOBALS.iter()) { + *key = create(Some(mem::transmute(dtor as unsafe extern fn(*mut u64)))); + set(*key, global as *const _ as *mut _); + } + + // Initialize cannary + CANNARY = Box::into_raw(Box::new(0u64)); + } +} From b8c5e7fd0eead08be1c6ba7bbb20104b1c266cd8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 20:18:42 -0700 Subject: [PATCH 2/7] refactor pointer handling in binops --- src/operator.rs | 112 +++++++++++++++++++----------------------------- src/value.rs | 40 ++++++++++++++--- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index 0303fbe1f79f..1c8c1a5d490c 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -146,45 +146,52 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; - // Offset is handled early, before we dispatch to unrelated_ptr_ops. We have to also catch the case where both arguments *are* convertible to integers. - if bin_op == Offset { - if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) { - let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; - let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; - return Ok((ptr, false)); - } else { - bug!("Offset used with wrong type"); - } - } - - // unrelated pointer ops - let op: Option bool> = match bin_op { - Eq => Some(PrimVal::eq), - Ne => Some(PrimVal::ne), - _ => None, - }; - if let Some(op) = op { - // only floats can't be binary compared - let ok = left_kind != F32 && left_kind != F64; - let ok = ok && right_kind != F32 && right_kind != F64; - if ok { - return Ok((PrimVal::from_bool(op(&left, &right)), false)); - } - } - - - if let (Ok(left), Ok(right)) = (left.to_ptr(), right.to_ptr()) { - if left.alloc_id == right.alloc_id { - return self.ptr_ops( - bin_op, - left.offset, - right.offset, - ); - } else { - return Err(EvalError::InvalidPointerMath); + // I: Handle operations that support pointers + let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); + let isize = PrimValKind::from_int_size(self.memory.pointer_size()); + if !left_kind.is_float() && !right_kind.is_float() { + match bin_op { + Offset if left_kind == Ptr && right_kind == usize => { + let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; + let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?; + return Ok((ptr, false)); + }, + // These work on anything + Eq if left_kind == right_kind => { + return Ok((PrimVal::from_bool(left == right), false)); + } + Ne if left_kind == right_kind => { + return Ok((PrimVal::from_bool(left != right), false)); + } + // These need both pointers to be in the same allocation + Lt | Le | Gt | Ge | Sub + if left_kind == right_kind + && (left_kind == Ptr || left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_ptr() => { + let left = left.to_ptr()?; + let right = right.to_ptr()?; + if left.alloc_id == right.alloc_id { + let res = match bin_op { + Lt => left.offset < right.offset, + Le => left.offset <= right.offset, + Gt => left.offset > right.offset, + Ge => left.offset >= right.offset, + Sub => { + return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset); + } + _ => bug!("We already established it has to be one of these operators."), + }; + return Ok((PrimVal::from_bool(res), false)); + } else { + // Both are pointers, but from different allocations. + return Err(EvalError::InvalidPointerMath); + } + } + _ => {} } } + // II: From now on, everything must be bytes, no pointers let l = left.to_bytes()?; let r = right.to_bytes()?; @@ -229,8 +236,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (Div, F64) => f64_arithmetic!(/, l, r), (Rem, F64) => f64_arithmetic!(%, l, r), - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)), (Lt, _) => PrimVal::from_bool(l < r), (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)), @@ -258,37 +263,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((val, false)) } - - fn ptr_ops( - &self, - bin_op: mir::BinOp, - left: u64, - right: u64, - ) -> EvalResult<'tcx, (PrimVal, bool)> { - use rustc::mir::BinOp::*; - - let val = match bin_op { - Eq => PrimVal::from_bool(left == right), - Ne => PrimVal::from_bool(left != right), - Lt | Le | Gt | Ge => { - PrimVal::from_bool(match bin_op { - Lt => left < right, - Le => left <= right, - Gt => left > right, - Ge => left >= right, - _ => bug!("We already established it has to be a comparison operator."), - }) - } - Sub => { - let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); - return int_arithmetic!(usize, overflowing_sub, left, right); - } - _ => { - return Err(EvalError::ReadPointerAsBytes); - } - }; - Ok((val, false)) - } } pub fn unary_op<'tcx>( diff --git a/src/value.rs b/src/value.rs index ef11c7a8e475..2f00d7eb1dff 100644 --- a/src/value.rs +++ b/src/value.rs @@ -160,6 +160,27 @@ impl<'tcx> PrimVal { } } + pub fn is_bytes(self) -> bool { + match self { + PrimVal::Bytes(_) => true, + _ => false, + } + } + + pub fn is_ptr(self) -> bool { + match self { + PrimVal::Ptr(_) => true, + _ => false, + } + } + + pub fn is_undef(self) -> bool { + match self { + PrimVal::Undef => true, + _ => false, + } + } + pub fn to_u128(self) -> EvalResult<'tcx, u128> { self.to_bytes() } @@ -252,14 +273,11 @@ pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalR } pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - if let Some(res) = val.checked_add(i) { - if res as u128 >= (1u128 << layout.pointer_size.bits()) { - Err(EvalError::OverflowingMath) - } else { - Ok(res) - } - } else { + let res = val.checked_add(i).ok_or(EvalError::OverflowingMath)?; + if res as u128 >= (1u128 << layout.pointer_size.bits()) { Err(EvalError::OverflowingMath) + } else { + Ok(res) } } @@ -284,6 +302,14 @@ impl PrimValKind { } } + pub fn is_float(self) -> bool { + use self::PrimValKind::*; + match self { + F32 | F64 => true, + _ => false, + } + } + pub fn from_uint_size(size: u64) -> Self { match size { 1 => PrimValKind::U8, From 7b1582b383ba98ad458001c332004b500dd39eed Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 21:38:43 -0700 Subject: [PATCH 3/7] permit integer addition and subtraction on ptr-integers --- src/memory.rs | 10 +++++++++ src/operator.rs | 37 +++++++++++++++++++++++++++++++++ src/value.rs | 28 +++++++++++++++++++------ tests/run-pass/ptr_int_casts.rs | 19 ++++++++++++++++- 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index ac2d391f4217..b6a823736875 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -64,10 +64,20 @@ impl Pointer { Pointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout)) } + pub fn overflowing_signed_offset<'tcx>(self, i: i128, layout: &TargetDataLayout) -> (Self, bool) { + let (res, over) = value::overflowing_signed_offset(self.offset, i, layout); + (Pointer::new(self.alloc_id, res), over) + } + pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { Ok(Pointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?)) } + pub fn overflowing_offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> (Self, bool) { + let (res, over) = value::overflowing_offset(self.offset, i, layout); + (Pointer::new(self.alloc_id, res), over) + } + pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> { Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?)) } diff --git a/src/operator.rs b/src/operator.rs index 1c8c1a5d490c..adcd237e819f 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -130,6 +130,19 @@ macro_rules! f64_arithmetic { ) } +macro_rules! ptr_add { + ($signed:expr, $ptr:expr, $int:expr, $layout:expr) => ({ + let ptr = $ptr; + let int = $int; + let (res, over) = if $signed { + ptr.overflowing_signed_offset(int as i128, $layout) + } else { + ptr.overflowing_offset(int as u64, $layout) + }; + (PrimVal::Ptr(res), over) + }) +} + impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( @@ -145,6 +158,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; + //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); // I: Handle operations that support pointers let usize = PrimValKind::from_uint_size(self.memory.pointer_size()); @@ -187,6 +201,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { return Err(EvalError::InvalidPointerMath); } } + // These work if one operand is a pointer, the other an integer + Sub + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_bytes() => { + let left = left.to_ptr()?; + let right = right.to_bytes()? as i128; // this cast is fine as the kind is max. 64bit + let (res, over) = left.overflowing_signed_offset(-right, self.memory.layout); + return Ok((PrimVal::Ptr(res), over)) + } + Add + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_ptr() && right.is_bytes() => { + let left = left.to_ptr()?; + let right = right.to_bytes()?; + return Ok(ptr_add!(left_kind == isize, left, right, self.memory.layout)); + } + Add + if left_kind == right_kind && (left_kind == usize || left_kind == isize) + && left.is_bytes() && right.is_ptr() => { + let left = left.to_bytes()?; + let right = right.to_ptr()?; + return Ok(ptr_add!(left_kind == isize, right, left, self.memory.layout)); + } _ => {} } } diff --git a/src/value.rs b/src/value.rs index 2f00d7eb1dff..519c048f3ef8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -260,21 +260,37 @@ impl<'tcx> PrimVal { } } -pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { +// Overflow checking only works properly on the range from -u64 to +u64. +pub fn overflowing_signed_offset<'tcx>(val: u64, i: i128, layout: &TargetDataLayout) -> (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.checked_sub(n).ok_or(EvalError::OverflowingMath) + val.overflowing_sub(n) } else { - offset(val, i as u64, layout) + overflowing_offset(val, i as u64, layout) + } +} + +pub fn overflowing_offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> (u64, bool) { + let (res, over) = val.overflowing_add(i); + ((res as u128 % (1u128 << layout.pointer_size.bits())) as u64, + over || res as u128 >= (1u128 << layout.pointer_size.bits())) +} + +pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { + let (res, over) = overflowing_signed_offset(val, i as i128, layout); + if over { + Err(EvalError::OverflowingMath) + } else { + Ok(res) } } pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> { - let res = val.checked_add(i).ok_or(EvalError::OverflowingMath)?; - if res as u128 >= (1u128 << layout.pointer_size.bits()) { + let (res, over) = overflowing_offset(val, i, layout); + if over { Err(EvalError::OverflowingMath) } else { Ok(res) @@ -282,7 +298,7 @@ pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<' } pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 { - (val.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64 + overflowing_signed_offset(val, i as i128, layout).0 } impl PrimValKind { diff --git a/tests/run-pass/ptr_int_casts.rs b/tests/run-pass/ptr_int_casts.rs index e245cb22475d..b7b17089efc7 100644 --- a/tests/run-pass/ptr_int_casts.rs +++ b/tests/run-pass/ptr_int_casts.rs @@ -1,15 +1,32 @@ +use std::mem; + fn eq_ref(x: &T, y: &T) -> bool { x as *const _ == y as *const _ } +fn f() -> i32 { 42 } + fn main() { // int-ptr-int assert_eq!(1 as *const i32 as usize, 1); + assert_eq!((1 as *const i32).wrapping_offset(4) as usize, 1 + 4*4); { // ptr-int-ptr let x = 13; - let y = &x as *const _ as usize; + let mut y = &x as *const _ as usize; + y += 13; + y -= 13; let y = y as *const _; assert!(eq_ref(&x, unsafe { &*y })); } + + { // fnptr-int-fnptr + let x : fn() -> i32 = f; + let y : *mut u8 = unsafe { mem::transmute(x) }; + let mut y = y as usize; + y += 13; + y -= 13; + let x : fn() -> i32 = unsafe { mem::transmute(y as *mut u8) }; + assert_eq!(x(), 42); + } } From 78aa93fa10ae3c0d34e8178c810f89f79d879b9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 21:45:51 -0700 Subject: [PATCH 4/7] correctly reject functions pointers that had arithmetic done to them --- src/memory.rs | 11 +++++++---- src/terminator/mod.rs | 8 ++++---- src/traits.rs | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index b6a823736875..4cf3ecb8215a 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -480,11 +480,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { } } - pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, ty::Instance<'tcx>> { - debug!("reading fn ptr: {}", id); - match self.functions.get(&id) { + pub fn get_fn(&self, ptr: Pointer) -> EvalResult<'tcx, ty::Instance<'tcx>> { + if ptr.offset != 0 { + return Err(EvalError::InvalidFunctionPointer); + } + debug!("reading fn ptr: {}", ptr.alloc_id); + match self.functions.get(&ptr.alloc_id) { Some(&fndef) => Ok(fndef), - None => match self.alloc_map.get(&id) { + None => match self.alloc_map.get(&ptr.alloc_id) { Some(_) => Err(EvalError::ExecuteMemory), None => Err(EvalError::InvalidFunctionPointer), } diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 636cf2ad0b5e..22b1bf309ce8 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -65,7 +65,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let (fn_def, sig) = match func_ty.sty { ty::TyFnPtr(sig) => { let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?; - let instance = self.memory.get_fn(fn_ptr.alloc_id)?; + let instance = self.memory.get_fn(fn_ptr)?; let instance_ty = instance.def.def_ty(self.tcx); let instance_ty = self.monomorphize(instance_ty, instance.substs); match instance_ty.sty { @@ -388,7 +388,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { 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), self.memory.layout)?)?; - let instance = self.memory.get_fn(fn_ptr.to_ptr()?.alloc_id)?; + let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?; let mut arg_operands = arg_operands.to_vec(); let ty = self.operand_ty(&arg_operands[0]); let ty = self.get_field_ty(ty, 0)?; @@ -596,7 +596,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8); let f = args[0].read_ptr(&self.memory)?.to_ptr()?; let data = args[1].read_ptr(&self.memory)?; - let f_instance = self.memory.get_fn(f.alloc_id)?; + let f_instance = self.memory.get_fn(f)?; self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?; // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors, @@ -723,7 +723,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { // Extract the function type out of the signature (that seems easier than constructing it ourselves...) let dtor = match args[1].read_ptr(&self.memory)? { - PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr.alloc_id)?), + PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), PrimVal::Bytes(0) => None, PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), PrimVal::Undef => return Err(EvalError::ReadUndefBytes), diff --git a/src/traits.rs b/src/traits.rs index 3862e8c631a6..680776968f98 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -78,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? { // some values don't need to call a drop impl, so the value is null Value::ByVal(PrimVal::Bytes(0)) => Ok(None), - Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn.alloc_id).map(Some), + Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn).map(Some), _ => Err(EvalError::ReadBytesAsPointer), } } From 6eafb10b870b4b7fad784402a76d6cef046f5928 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 21:54:42 -0700 Subject: [PATCH 5/7] add test for function pointer offsets --- tests/compile-fail/fn_ptr_offset.rs | 11 +++++++++++ tests/run-pass/thread-local.rs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/compile-fail/fn_ptr_offset.rs diff --git a/tests/compile-fail/fn_ptr_offset.rs b/tests/compile-fail/fn_ptr_offset.rs new file mode 100644 index 000000000000..2d240b6a55ad --- /dev/null +++ b/tests/compile-fail/fn_ptr_offset.rs @@ -0,0 +1,11 @@ +use std::mem; + +fn f() {} + +fn main() { + let x : fn() = f; + let y : *mut u8 = unsafe { mem::transmute(x) }; + let y = y.wrapping_offset(1); + let x : fn() = unsafe { mem::transmute(y) }; + x(); //~ ERROR: tried to use an integer pointer or a dangling pointer as a function pointer +} diff --git a/tests/run-pass/thread-local.rs b/tests/run-pass/thread-local.rs index 003fd1ad4c00..34aeef23b1ad 100644 --- a/tests/run-pass/thread-local.rs +++ b/tests/run-pass/thread-local.rs @@ -42,7 +42,7 @@ unsafe extern fn dtor(mut ptr: *mut u64) { } // Check if the records matches what we expect. If yes, clear the cannary. - // If the record is wrong, the cannary will ever get cleared, leading to a leak -> test fails. + // If the record is wrong, the cannary will never get cleared, leading to a leak -> test fails. // If the record is incomplete (i.e., more dtor calls happen), the check at the beginning of this function will fail -> test fails. // The correct sequence is: First key 0, then key 1, then key 0. if RECORD == 0_1_0 { From 894306e47d1bf5d7c4328d762e0e2c94dbeea2fe Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 22:59:47 -0700 Subject: [PATCH 6/7] refactor pointer arithmetic handling --- src/operator.rs | 57 +++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/operator.rs b/src/operator.rs index adcd237e819f..7cba12594f9b 100644 --- a/src/operator.rs +++ b/src/operator.rs @@ -3,6 +3,7 @@ use rustc::ty::{self, Ty}; use error::{EvalError, EvalResult}; use eval_context::EvalContext; +use memory::Pointer; use lvalue::Lvalue; use value::{ PrimVal, @@ -130,19 +131,6 @@ macro_rules! f64_arithmetic { ) } -macro_rules! ptr_add { - ($signed:expr, $ptr:expr, $int:expr, $layout:expr) => ({ - let ptr = $ptr; - let int = $int; - let (res, over) = if $signed { - ptr.overflowing_signed_offset(int as i128, $layout) - } else { - ptr.overflowing_offset(int as u64, $layout) - }; - (PrimVal::Ptr(res), over) - }) -} - impl<'a, 'tcx> EvalContext<'a, 'tcx> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( @@ -202,27 +190,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } // These work if one operand is a pointer, the other an integer - Sub + Add | Sub if left_kind == right_kind && (left_kind == usize || left_kind == isize) && left.is_ptr() && right.is_bytes() => { - let left = left.to_ptr()?; - let right = right.to_bytes()? as i128; // this cast is fine as the kind is max. 64bit - let (res, over) = left.overflowing_signed_offset(-right, self.memory.layout); - return Ok((PrimVal::Ptr(res), over)) - } - Add - if left_kind == right_kind && (left_kind == usize || left_kind == isize) - && left.is_ptr() && right.is_bytes() => { - let left = left.to_ptr()?; - let right = right.to_bytes()?; - return Ok(ptr_add!(left_kind == isize, left, right, self.memory.layout)); + // Cast to i128 is fine as we checked the kind to be ptr-sized + let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?; + return Ok((PrimVal::Ptr(res), over)); } Add if left_kind == right_kind && (left_kind == usize || left_kind == isize) && left.is_bytes() && right.is_ptr() => { - let left = left.to_bytes()?; - let right = right.to_ptr()?; - return Ok(ptr_add!(left_kind == isize, right, left, self.memory.layout)); + // This is a commutative operation, just swap the operands + let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?; + return Ok((PrimVal::Ptr(res), over)); } _ => {} } @@ -300,6 +280,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok((val, false)) } + + fn ptr_int_arithmetic( + &self, + bin_op: mir::BinOp, + left: Pointer, + right: i128, + signed: bool, + ) -> EvalResult<'tcx, (Pointer, bool)> { + use rustc::mir::BinOp::*; + + Ok(match bin_op { + Sub => + // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter + left.overflowing_signed_offset(-right, self.memory.layout), + Add if signed => + left.overflowing_signed_offset(right, self.memory.layout), + Add if !signed => + left.overflowing_offset(right as u64, self.memory.layout), + _ => bug!("ptr_int_arithmetic called on unsupported operation") + }) + } } pub fn unary_op<'tcx>( From 7b2b0dd56c2ab8d5dc4e9c7e2bbd0c4128ba4f5d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 21 Jun 2017 23:09:34 -0700 Subject: [PATCH 7/7] test HashMap creation in libstd-MIR, and make it work again --- src/terminator/intrinsic.rs | 3 ++- tests/{run-pass => run-pass-fullmir}/hashmap.rs | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) rename tests/{run-pass => run-pass-fullmir}/hashmap.rs (73%) diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index f6ef46ecbfb8..8ede9fa7559b 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -424,9 +424,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ty_align = self.type_align(ty)?; let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8; let size = self.type_size(ty)?.expect("write_bytes() type must be sized"); - let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?; + let ptr = arg_vals[0].read_ptr(&self.memory)?; let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?; if count > 0 { + let ptr = ptr.to_ptr()?; self.memory.check_align(ptr, ty_align, size * count)?; self.memory.write_repeat(ptr, val_byte, size * count)?; } diff --git a/tests/run-pass/hashmap.rs b/tests/run-pass-fullmir/hashmap.rs similarity index 73% rename from tests/run-pass/hashmap.rs rename to tests/run-pass-fullmir/hashmap.rs index 775dee252f62..c0c396240733 100644 --- a/tests/run-pass/hashmap.rs +++ b/tests/run-pass-fullmir/hashmap.rs @@ -1,10 +1,6 @@ use std::collections::{self, HashMap}; use std::hash::BuildHasherDefault; -// This disables the test completely: -// ignore-stage1 -// TODO: The tests actually passes against rustc and miri with MIR-libstd, but right now, we cannot express that in the test flags - fn main() { let map : HashMap> = Default::default(); assert_eq!(map.values().fold(0, |x, y| x+y), 0);