Skip to content

Commit 522ac49

Browse files
authored
Merge pull request rust-lang#206 from RalfJung/ptrs
Pointer Arithmetic
2 parents 046136a + 7b2b0dd commit 522ac49

File tree

11 files changed

+250
-109
lines changed

11 files changed

+250
-109
lines changed

src/memory.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,20 @@ impl Pointer {
6464
Pointer::new(self.alloc_id, value::wrapping_signed_offset(self.offset, i, layout))
6565
}
6666

67+
pub fn overflowing_signed_offset<'tcx>(self, i: i128, layout: &TargetDataLayout) -> (Self, bool) {
68+
let (res, over) = value::overflowing_signed_offset(self.offset, i, layout);
69+
(Pointer::new(self.alloc_id, res), over)
70+
}
71+
6772
pub fn signed_offset<'tcx>(self, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
6873
Ok(Pointer::new(self.alloc_id, value::signed_offset(self.offset, i, layout)?))
6974
}
7075

76+
pub fn overflowing_offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> (Self, bool) {
77+
let (res, over) = value::overflowing_offset(self.offset, i, layout);
78+
(Pointer::new(self.alloc_id, res), over)
79+
}
80+
7181
pub fn offset<'tcx>(self, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, Self> {
7282
Ok(Pointer::new(self.alloc_id, value::offset(self.offset, i, layout)?))
7383
}
@@ -470,11 +480,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
470480
}
471481
}
472482

473-
pub fn get_fn(&self, id: AllocId) -> EvalResult<'tcx, ty::Instance<'tcx>> {
474-
debug!("reading fn ptr: {}", id);
475-
match self.functions.get(&id) {
483+
pub fn get_fn(&self, ptr: Pointer) -> EvalResult<'tcx, ty::Instance<'tcx>> {
484+
if ptr.offset != 0 {
485+
return Err(EvalError::InvalidFunctionPointer);
486+
}
487+
debug!("reading fn ptr: {}", ptr.alloc_id);
488+
match self.functions.get(&ptr.alloc_id) {
476489
Some(&fndef) => Ok(fndef),
477-
None => match self.alloc_map.get(&id) {
490+
None => match self.alloc_map.get(&ptr.alloc_id) {
478491
Some(_) => Err(EvalError::ExecuteMemory),
479492
None => Err(EvalError::InvalidFunctionPointer),
480493
}

src/operator.rs

Lines changed: 76 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc::ty::{self, Ty};
33

44
use error::{EvalError, EvalResult};
55
use eval_context::EvalContext;
6+
use memory::Pointer;
67
use lvalue::Lvalue;
78
use value::{
89
PrimVal,
@@ -145,46 +146,69 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
145146

146147
let left_kind = self.ty_to_primval_kind(left_ty)?;
147148
let right_kind = self.ty_to_primval_kind(right_ty)?;
148-
149-
// 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.
150-
if bin_op == Offset {
151-
if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) {
152-
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
153-
let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?;
154-
return Ok((ptr, false));
155-
} else {
156-
bug!("Offset used with wrong type");
157-
}
158-
}
159-
160-
// unrelated pointer ops
161-
let op: Option<fn(&PrimVal, &PrimVal) -> bool> = match bin_op {
162-
Eq => Some(PrimVal::eq),
163-
Ne => Some(PrimVal::ne),
164-
_ => None,
165-
};
166-
if let Some(op) = op {
167-
// only floats can't be binary compared
168-
let ok = left_kind != F32 && left_kind != F64;
169-
let ok = ok && right_kind != F32 && right_kind != F64;
170-
if ok {
171-
return Ok((PrimVal::from_bool(op(&left, &right)), false));
172-
}
173-
}
174-
175-
176-
if let (Ok(left), Ok(right)) = (left.to_ptr(), right.to_ptr()) {
177-
if left.alloc_id == right.alloc_id {
178-
return self.ptr_ops(
179-
bin_op,
180-
left.offset,
181-
right.offset,
182-
);
183-
} else {
184-
return Err(EvalError::InvalidPointerMath);
149+
//trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
150+
151+
// I: Handle operations that support pointers
152+
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
153+
let isize = PrimValKind::from_int_size(self.memory.pointer_size());
154+
if !left_kind.is_float() && !right_kind.is_float() {
155+
match bin_op {
156+
Offset if left_kind == Ptr && right_kind == usize => {
157+
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
158+
let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?;
159+
return Ok((ptr, false));
160+
},
161+
// These work on anything
162+
Eq if left_kind == right_kind => {
163+
return Ok((PrimVal::from_bool(left == right), false));
164+
}
165+
Ne if left_kind == right_kind => {
166+
return Ok((PrimVal::from_bool(left != right), false));
167+
}
168+
// These need both pointers to be in the same allocation
169+
Lt | Le | Gt | Ge | Sub
170+
if left_kind == right_kind
171+
&& (left_kind == Ptr || left_kind == usize || left_kind == isize)
172+
&& left.is_ptr() && right.is_ptr() => {
173+
let left = left.to_ptr()?;
174+
let right = right.to_ptr()?;
175+
if left.alloc_id == right.alloc_id {
176+
let res = match bin_op {
177+
Lt => left.offset < right.offset,
178+
Le => left.offset <= right.offset,
179+
Gt => left.offset > right.offset,
180+
Ge => left.offset >= right.offset,
181+
Sub => {
182+
return int_arithmetic!(left_kind, overflowing_sub, left.offset, right.offset);
183+
}
184+
_ => bug!("We already established it has to be one of these operators."),
185+
};
186+
return Ok((PrimVal::from_bool(res), false));
187+
} else {
188+
// Both are pointers, but from different allocations.
189+
return Err(EvalError::InvalidPointerMath);
190+
}
191+
}
192+
// These work if one operand is a pointer, the other an integer
193+
Add | Sub
194+
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
195+
&& left.is_ptr() && right.is_bytes() => {
196+
// Cast to i128 is fine as we checked the kind to be ptr-sized
197+
let (res, over) = self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize)?;
198+
return Ok((PrimVal::Ptr(res), over));
199+
}
200+
Add
201+
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
202+
&& left.is_bytes() && right.is_ptr() => {
203+
// This is a commutative operation, just swap the operands
204+
let (res, over) = self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize)?;
205+
return Ok((PrimVal::Ptr(res), over));
206+
}
207+
_ => {}
185208
}
186209
}
187210

211+
// II: From now on, everything must be bytes, no pointers
188212
let l = left.to_bytes()?;
189213
let r = right.to_bytes()?;
190214

@@ -229,8 +253,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
229253
(Div, F64) => f64_arithmetic!(/, l, r),
230254
(Rem, F64) => f64_arithmetic!(%, l, r),
231255

232-
(Eq, _) => PrimVal::from_bool(l == r),
233-
(Ne, _) => PrimVal::from_bool(l != r),
234256
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
235257
(Lt, _) => PrimVal::from_bool(l < r),
236258
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
@@ -259,35 +281,25 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
259281
Ok((val, false))
260282
}
261283

262-
fn ptr_ops(
284+
fn ptr_int_arithmetic(
263285
&self,
264286
bin_op: mir::BinOp,
265-
left: u64,
266-
right: u64,
267-
) -> EvalResult<'tcx, (PrimVal, bool)> {
287+
left: Pointer,
288+
right: i128,
289+
signed: bool,
290+
) -> EvalResult<'tcx, (Pointer, bool)> {
268291
use rustc::mir::BinOp::*;
269292

270-
let val = match bin_op {
271-
Eq => PrimVal::from_bool(left == right),
272-
Ne => PrimVal::from_bool(left != right),
273-
Lt | Le | Gt | Ge => {
274-
PrimVal::from_bool(match bin_op {
275-
Lt => left < right,
276-
Le => left <= right,
277-
Gt => left > right,
278-
Ge => left >= right,
279-
_ => bug!("We already established it has to be a comparison operator."),
280-
})
281-
}
282-
Sub => {
283-
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
284-
return int_arithmetic!(usize, overflowing_sub, left, right);
285-
}
286-
_ => {
287-
return Err(EvalError::ReadPointerAsBytes);
288-
}
289-
};
290-
Ok((val, false))
293+
Ok(match bin_op {
294+
Sub =>
295+
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
296+
left.overflowing_signed_offset(-right, self.memory.layout),
297+
Add if signed =>
298+
left.overflowing_signed_offset(right, self.memory.layout),
299+
Add if !signed =>
300+
left.overflowing_offset(right as u64, self.memory.layout),
301+
_ => bug!("ptr_int_arithmetic called on unsupported operation")
302+
})
291303
}
292304
}
293305

src/terminator/intrinsic.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,9 +424,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
424424
let ty_align = self.type_align(ty)?;
425425
let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8;
426426
let size = self.type_size(ty)?.expect("write_bytes() type must be sized");
427-
let ptr = arg_vals[0].read_ptr(&self.memory)?.to_ptr()?;
427+
let ptr = arg_vals[0].read_ptr(&self.memory)?;
428428
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
429429
if count > 0 {
430+
let ptr = ptr.to_ptr()?;
430431
self.memory.check_align(ptr, ty_align, size * count)?;
431432
self.memory.write_repeat(ptr, val_byte, size * count)?;
432433
}

src/terminator/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
6565
let (fn_def, sig) = match func_ty.sty {
6666
ty::TyFnPtr(sig) => {
6767
let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?;
68-
let instance = self.memory.get_fn(fn_ptr.alloc_id)?;
68+
let instance = self.memory.get_fn(fn_ptr)?;
6969
let instance_ty = instance.def.def_ty(self.tcx);
7070
let instance_ty = self.monomorphize(instance_ty, instance.substs);
7171
match instance_ty.sty {
@@ -388,7 +388,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
388388
let ptr_size = self.memory.pointer_size();
389389
let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?;
390390
let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?;
391-
let instance = self.memory.get_fn(fn_ptr.to_ptr()?.alloc_id)?;
391+
let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?;
392392
let mut arg_operands = arg_operands.to_vec();
393393
let ty = self.operand_ty(&arg_operands[0]);
394394
let ty = self.get_field_ty(ty, 0)?;
@@ -596,7 +596,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
596596
let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
597597
let f = args[0].read_ptr(&self.memory)?.to_ptr()?;
598598
let data = args[1].read_ptr(&self.memory)?;
599-
let f_instance = self.memory.get_fn(f.alloc_id)?;
599+
let f_instance = self.memory.get_fn(f)?;
600600
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
601601

602602
// Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors,
@@ -614,7 +614,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
614614
let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
615615
self.write_primval(arg_dest, data, u8_ptr_ty)?;
616616

617-
// We ourselbes return 0
617+
// We ourselves return 0
618618
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
619619

620620
// Don't fall through
@@ -723,7 +723,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
723723

724724
// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
725725
let dtor = match args[1].read_ptr(&self.memory)? {
726-
PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr.alloc_id)?),
726+
PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?),
727727
PrimVal::Bytes(0) => None,
728728
PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer),
729729
PrimVal::Undef => return Err(EvalError::ReadUndefBytes),

src/traits.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
7878
match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? {
7979
// some values don't need to call a drop impl, so the value is null
8080
Value::ByVal(PrimVal::Bytes(0)) => Ok(None),
81-
Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn.alloc_id).map(Some),
81+
Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn).map(Some),
8282
_ => Err(EvalError::ReadBytesAsPointer),
8383
}
8484
}

src/value.rs

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,27 @@ impl<'tcx> PrimVal {
160160
}
161161
}
162162

163+
pub fn is_bytes(self) -> bool {
164+
match self {
165+
PrimVal::Bytes(_) => true,
166+
_ => false,
167+
}
168+
}
169+
170+
pub fn is_ptr(self) -> bool {
171+
match self {
172+
PrimVal::Ptr(_) => true,
173+
_ => false,
174+
}
175+
}
176+
177+
pub fn is_undef(self) -> bool {
178+
match self {
179+
PrimVal::Undef => true,
180+
_ => false,
181+
}
182+
}
183+
163184
pub fn to_u128(self) -> EvalResult<'tcx, u128> {
164185
self.to_bytes()
165186
}
@@ -239,32 +260,45 @@ impl<'tcx> PrimVal {
239260
}
240261
}
241262

242-
pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> {
263+
// Overflow checking only works properly on the range from -u64 to +u64.
264+
pub fn overflowing_signed_offset<'tcx>(val: u64, i: i128, layout: &TargetDataLayout) -> (u64, bool) {
243265
// FIXME: is it possible to over/underflow here?
244266
if i < 0 {
245267
// trickery to ensure that i64::min_value() works fine
246268
// this formula only works for true negative values, it panics for zero!
247269
let n = u64::max_value() - (i as u64) + 1;
248-
val.checked_sub(n).ok_or(EvalError::OverflowingMath)
270+
val.overflowing_sub(n)
249271
} else {
250-
offset(val, i as u64, layout)
272+
overflowing_offset(val, i as u64, layout)
251273
}
252274
}
253275

254-
pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> {
255-
if let Some(res) = val.checked_add(i) {
256-
if res as u128 >= (1u128 << layout.pointer_size.bits()) {
257-
Err(EvalError::OverflowingMath)
258-
} else {
259-
Ok(res)
260-
}
276+
pub fn overflowing_offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> (u64, bool) {
277+
let (res, over) = val.overflowing_add(i);
278+
((res as u128 % (1u128 << layout.pointer_size.bits())) as u64,
279+
over || res as u128 >= (1u128 << layout.pointer_size.bits()))
280+
}
281+
282+
pub fn signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> {
283+
let (res, over) = overflowing_signed_offset(val, i as i128, layout);
284+
if over {
285+
Err(EvalError::OverflowingMath)
261286
} else {
287+
Ok(res)
288+
}
289+
}
290+
291+
pub fn offset<'tcx>(val: u64, i: u64, layout: &TargetDataLayout) -> EvalResult<'tcx, u64> {
292+
let (res, over) = overflowing_offset(val, i, layout);
293+
if over {
262294
Err(EvalError::OverflowingMath)
295+
} else {
296+
Ok(res)
263297
}
264298
}
265299

266300
pub fn wrapping_signed_offset<'tcx>(val: u64, i: i64, layout: &TargetDataLayout) -> u64 {
267-
(val.wrapping_add(i as u64) as u128 % (1u128 << layout.pointer_size.bits())) as u64
301+
overflowing_signed_offset(val, i as i128, layout).0
268302
}
269303

270304
impl PrimValKind {
@@ -284,6 +318,14 @@ impl PrimValKind {
284318
}
285319
}
286320

321+
pub fn is_float(self) -> bool {
322+
use self::PrimValKind::*;
323+
match self {
324+
F32 | F64 => true,
325+
_ => false,
326+
}
327+
}
328+
287329
pub fn from_uint_size(size: u64) -> Self {
288330
match size {
289331
1 => PrimValKind::U8,

tests/compile-fail/fn_ptr_offset.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use std::mem;
2+
3+
fn f() {}
4+
5+
fn main() {
6+
let x : fn() = f;
7+
let y : *mut u8 = unsafe { mem::transmute(x) };
8+
let y = y.wrapping_offset(1);
9+
let x : fn() = unsafe { mem::transmute(y) };
10+
x(); //~ ERROR: tried to use an integer pointer or a dangling pointer as a function pointer
11+
}

0 commit comments

Comments
 (0)