fix bitops being accidentally allowed on pointers from the same allocation
This commit is contained in:
parent
a6e6a6fd29
commit
36505c7b40
102
src/operator.rs
102
src/operator.rs
@ -155,57 +155,48 @@ fn normalize(val: PrimVal) -> PrimVal {
|
||||
}
|
||||
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.
|
||||
if bin_op == Offset {
|
||||
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.to_ptr()?, pointee_ty, right.to_bytes()? as i64)?;
|
||||
return Ok((PrimVal::Ptr(ptr), false));
|
||||
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.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) {
|
||||
(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)) => {
|
||||
if left_ptr.alloc_id == right_ptr.alloc_id {
|
||||
// If the pointers are into the same allocation, fall through to the more general
|
||||
// match later, which will do comparisons on the pointer offsets.
|
||||
(left_ptr.offset as u128, right_ptr.offset as u128)
|
||||
} else {
|
||||
return Ok((unrelated_ptr_ops(bin_op, left_ptr, right_ptr)?, false));
|
||||
}
|
||||
return self.ptr_ops(bin_op, left_ptr, left_kind, right_ptr, right_kind);
|
||||
}
|
||||
(PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) => {
|
||||
return self.ptr_ops(bin_op, ptr, left_kind, Pointer::from_int(bytes as u64), right_kind);
|
||||
}
|
||||
|
||||
(PrimVal::Ptr(ptr), PrimVal::Bytes(bytes)) |
|
||||
(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),
|
||||
};
|
||||
|
||||
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.
|
||||
if bin_op == Shl || bin_op == Shr {
|
||||
if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
|
||||
return match bin_op {
|
||||
Shl => int_shift!(left_kind, overflowing_shl, 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"),
|
||||
};
|
||||
}
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
@ -258,25 +249,58 @@ fn normalize(val: PrimVal) -> PrimVal {
|
||||
(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));
|
||||
}
|
||||
};
|
||||
|
||||
Ok((val, false))
|
||||
}
|
||||
}
|
||||
|
||||
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp, left: Pointer, right: Pointer) -> EvalResult<'tcx, PrimVal> {
|
||||
use rustc::mir::BinOp::*;
|
||||
match bin_op {
|
||||
Eq => Ok(PrimVal::from_bool(false)),
|
||||
Ne => Ok(PrimVal::from_bool(true)),
|
||||
Lt | Le | Gt | Ge => Err(EvalError::InvalidPointerMath),
|
||||
_ if left.to_int().is_ok() ^ right.to_int().is_ok() => {
|
||||
Err(EvalError::ReadPointerAsBytes)
|
||||
},
|
||||
_ => bug!(),
|
||||
fn ptr_ops(
|
||||
&self,
|
||||
bin_op: mir::BinOp,
|
||||
left: Pointer,
|
||||
left_kind: PrimValKind,
|
||||
right: Pointer,
|
||||
right_kind: PrimValKind,
|
||||
) -> EvalResult<'tcx, (PrimVal, bool)> {
|
||||
use rustc::mir::BinOp::*;
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,4 +243,12 @@ pub fn from_int_size(size: u64) -> Self {
|
||||
_ => bug!("can't make int with size {}", size),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ptr(self) -> bool {
|
||||
use self::PrimValKind::*;
|
||||
match self {
|
||||
Ptr | FnPtr => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
16
tests/compile-fail/overflowing-lsh-neg.rs
Normal file
16
tests/compile-fail/overflowing-lsh-neg.rs
Normal 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)
|
||||
}
|
7
tests/compile-fail/ptr_bitops.rs
Normal file
7
tests/compile-fail/ptr_bitops.rs
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user