1
Fork 0

fix bitops being accidentally allowed on pointers from the same allocation

This commit is contained in:
Ralf Jung 2017-06-05 17:11:51 -07:00
parent a6e6a6fd29
commit 36505c7b40
4 changed files with 94 additions and 39 deletions

View file

@ -155,57 +155,48 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
} }
let (left, right) = (normalize(left), normalize(right)); let (left, right) = (normalize(left), normalize(right));
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. // 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 bin_op == Offset {
let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty; if left_kind == Ptr && right_kind == PrimValKind::from_uint_size(self.memory.pointer_size()) {
let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?; let pointee_ty = left_ty.builtin_deref(true, ty::LvaluePreference::NoPreference).expect("Offset called on non-ptr type").ty;
return Ok((PrimVal::Ptr(ptr), false)); let ptr = self.pointer_offset(left.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?;
return Ok((PrimVal::Ptr(ptr), false));
} else {
bug!("Offset used with wrong type");
}
} }
let (l, r) = match (left, right) { let (l, r) = match (left, right) {
(PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes), (PrimVal::Bytes(left_bytes), PrimVal::Bytes(right_bytes)) => (left_bytes, right_bytes),
// One argument is a pointer value -- this is handled separately
(PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => { (PrimVal::Ptr(left_ptr), PrimVal::Ptr(right_ptr)) => {
if left_ptr.alloc_id == right_ptr.alloc_id { return self.ptr_ops(bin_op, left_ptr, left_kind, right_ptr, right_kind);
// If the pointers are into the same allocation, fall through to the more general }
// match later, which will do comparisons on the pointer offsets. (PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) => {
(left_ptr.offset as u128, right_ptr.offset as u128) return self.ptr_ops(bin_op, ptr, left_kind, Pointer::from_int(bytes as u64), right_kind);
} else {
return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false));
}
} }
(PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) |
(PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => { (PrimVal::Bytes(bytes), PrimVal::Ptr(ptr)) => {
return Ok((unrelated_ptr_ops(bin_op, ptr, Pointer::from_int(bytes as u64))?, false)); return self.ptr_ops(bin_op, Pointer::from_int(bytes as u64), left_kind, ptr, right_kind);
} }
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes), (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
}; };
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
// These ops can have an RHS with a different numeric type. // These ops can have an RHS with a different numeric type.
if bin_op == Shl || bin_op == Shr { if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
return match bin_op { return match bin_op {
Shl => int_shift!(left_kind, overflowing_shl, l, r as u32), Shl => int_shift!(left_kind, overflowing_shl, l, r as u32),
Shr => int_shift!(left_kind, overflowing_shr, l, r as u32), Shr => int_shift!(left_kind, overflowing_shr, l, r as u32),
_ => bug!("it has already been checked that this is a shift op"), _ => bug!("it has already been checked that this is a shift op"),
}; };
} }
if bin_op == Offset {
// We permit offset-by-0 in any case. Drop glue actually does this, and it's probably (TM) fine for LLVM.
if left_kind == PrimValKind::Ptr && right_kind.is_int() && r == 0 {
return Ok((PrimVal::Bytes(l), false));
} else {
let msg = format!("unimplemented Offset: {:?}, {:?}", left, right);
return Err(EvalError::Unimplemented(msg));
}
}
if left_kind != right_kind { if left_kind != right_kind {
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
return Err(EvalError::Unimplemented(msg)); return Err(EvalError::Unimplemented(msg));
} }
@ -258,25 +249,58 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r), (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
_ => { _ => {
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op); let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
return Err(EvalError::Unimplemented(msg)); return Err(EvalError::Unimplemented(msg));
} }
}; };
Ok((val, false)) Ok((val, false))
} }
}
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> { fn ptr_ops(
use rustc::mir::BinOp::*; &self,
match bin_op { bin_op: mir::BinOp,
Eq => Ok(PrimVal::from_bool(false)), left: Pointer,
Ne => Ok(PrimVal::from_bool(true)), left_kind: PrimValKind,
Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath), right: Pointer,
_ if left.to_int().is_ok() ^ right.to_int().is_ok() => { right_kind: PrimValKind,
Err(EvalError::ReadPointerAsBytes) ) -> EvalResult<'tcx, (PrimVal, bool)> {
}, use rustc::mir::BinOp::*;
_ => bug!(), use value::PrimValKind::*;
if left_kind != right_kind {
let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
return Err(EvalError::Unimplemented(msg));
}
let val = match (bin_op, left_kind) {
(Eq, k) if k.is_ptr() => PrimVal::from_bool(left == right),
(Ne, k) if k.is_ptr() => PrimVal::from_bool(left != right),
(Lt, k) | (Le, k) | (Gt, k) | (Ge, k) if k.is_ptr() => {
if left.alloc_id == right.alloc_id {
PrimVal::from_bool(match bin_op {
Lt => left.offset < right.offset,
Le => left.offset <= right.offset,
Gt => left.offset > right.offset,
Ge => left.offset >= right.offset,
_ => bug!("We already established it has to be a comparison operator."),
})
} else {
return Err(EvalError::InvalidPointerMath);
}
}
(Sub, k) if k == PrimValKind::from_uint_size(self.memory.pointer_size()) => {
if left.alloc_id == right.alloc_id {
return int_arithmetic!(k, overflowing_sub, left.offset, right.offset);
} else {
return Err(EvalError::InvalidPointerMath);
}
}
_ => {
return Err(EvalError::ReadPointerAsBytes);
}
};
Ok((val, false))
} }
} }

View file

@ -243,4 +243,12 @@ impl PrimValKind {
_ => bug!("can't make int with size {}", size), _ => bug!("can't make int with size {}", size),
} }
} }
pub fn is_ptr(self) -> bool {
use self::PrimValKind::*;
match self {
Ptr | FnPtr => true,
_ => false,
}
}
} }

View file

@ -0,0 +1,16 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(exceeding_bitshifts)]
#![allow(const_err)]
fn main() {
let _n = 2i64 << -1; //~ Overflow(Shl)
}

View file

@ -0,0 +1,7 @@
fn main() {
let bytes = [0i8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let one = bytes.as_ptr().wrapping_offset(1);
let three = bytes.as_ptr().wrapping_offset(3);
let res = (one as usize) | (three as usize); //~ ERROR a raw memory access tried to access part of a pointer value as raw bytes
println!("{}", res);
}