handle ptr-int casts explicitly in cast.rs

This commit is contained in:
Ralf Jung 2017-06-22 18:41:10 -07:00
parent 7b7f690274
commit f0c8df2291
4 changed files with 29 additions and 23 deletions

View File

@ -19,9 +19,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
F32 => self.cast_float(val.to_f32()? as f64, dest_ty),
F64 => self.cast_float(val.to_f64()?, dest_ty),
I8 | I16 | I32 | I64 | I128 => self.cast_signed_int(val.to_i128()?, dest_ty),
I8 | I16 | I32 | I64 | I128 => {
if val.is_ptr() {
self.cast_ptr(val, dest_ty)
} else {
self.cast_signed_int(val.to_i128()?, dest_ty)
}
},
Bool | Char | U8 | U16 | U32 | U64 | U128 => self.cast_int(val.to_u128()?, dest_ty, false),
Bool | Char | U8 | U16 | U32 | U64 | U128 => {
if val.is_ptr() {
self.cast_ptr(val, dest_ty)
} else {
self.cast_int(val.to_u128()?, dest_ty, false)
}
},
FnPtr | Ptr => self.cast_ptr(val, dest_ty),
}
@ -70,6 +82,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
TyChar => Err(EvalError::InvalidChar(v)),
// No alignment check needed for raw pointers
TyRawPtr(_) => Ok(PrimVal::Bytes(v % (1 << self.memory.pointer_size()))),
_ => Err(EvalError::Unimplemented(format!("int to {:?} cast", ty))),
@ -94,8 +107,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn cast_ptr(&self, ptr: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
match ty.sty {
TyRef(..) | TyRawPtr(_) | TyFnPtr(_) | TyInt(_) | TyUint(_) =>
// Casting to a reference or fn pointer is not permitted by rustc, no need to support it here.
TyRawPtr(_) | TyInt(IntTy::Is) | TyUint(UintTy::Us) =>
Ok(ptr),
TyInt(_) | TyUint(_) => Err(EvalError::ReadPointerAsBytes),
_ => Err(EvalError::Unimplemented(format!("ptr to {:?} cast", ty))),
}
}

View File

@ -21,7 +21,7 @@ pub enum EvalError<'tcx> {
access: bool,
allocation_size: u64,
},
NullPointerOutOfBounds,
InvalidNullPointerUsage,
ReadPointerAsBytes,
ReadBytesAsPointer,
InvalidPointerMath,
@ -84,8 +84,8 @@ impl<'tcx> Error for EvalError<'tcx> {
"invalid enum discriminant value read",
EvalError::PointerOutOfBounds { .. } =>
"pointer offset outside bounds of allocation",
EvalError::NullPointerOutOfBounds =>
"invalid NULL pointer offset",
EvalError::InvalidNullPointerUsage =>
"invalid use of NULL pointer",
EvalError::ReadPointerAsBytes =>
"a raw memory access tried to access part of a pointer value as raw bytes",
EvalError::ReadBytesAsPointer =>

View File

@ -717,19 +717,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
(Value::ByVal(_), _) => bug!("expected fat ptr"),
}
} else {
// First, try casting
let dest_val = self.value_to_primval(src, src_ty).and_then(
|src_val| { self.cast_primval(src_val, src_ty, dest_ty) })
// Alternatively, if the sizes are equal, try just reading at the target type
.or_else(|err| {
let size = self.type_size(src_ty)?;
if size.is_some() && size == self.type_size(dest_ty)? {
self.value_to_primval(src, dest_ty)
} else {
Err(err)
}
});
self.write_value(Value::ByVal(dest_val?), dest, dest_ty)?;
let src_val = self.value_to_primval(src, src_ty)?;
let dest_val = self.cast_primval(src_val, src_ty, dest_ty)?;
self.write_value(Value::ByVal(dest_val), dest, dest_ty)?;
}
}
@ -908,7 +898,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// allocation.
if ptr.is_null()? { // NULL pointers must only be offset by 0
return if offset == 0 { Ok(ptr) } else { Err(EvalError::NullPointerOutOfBounds) };
return if offset == 0 { Ok(ptr) } else { Err(EvalError::InvalidNullPointerUsage) };
}
// 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;
@ -919,7 +909,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.memory.check_bounds(ptr, false)?;
} else if ptr.is_null()? {
// We moved *to* a NULL pointer. That seems wrong, LLVM considers the NULL pointer its own small allocation. Reject this, for now.
return Err(EvalError::NullPointerOutOfBounds);
return Err(EvalError::InvalidNullPointerUsage);
}
Ok(ptr)
} else {

View File

@ -1,7 +1,8 @@
fn main() {
let x = &1;
// Casting down to u8 and back up to a pointer loses too much precision; this must not work.
let x = x as *const i32 as u8;
let x = x as *const i32; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes
let x = x as *const i32;
let x = x as u8; //~ ERROR: a raw memory access tried to access part of a pointer value as raw bytes
let x = x as *const i32;
let _ = unsafe { *x };
}