centralize Scalar size sanity checks and also do them in release builds

This commit is contained in:
Ralf Jung 2019-05-26 14:13:12 +02:00
parent 3defb3f18f
commit aad13a176a
8 changed files with 54 additions and 79 deletions

View File

@ -2,7 +2,6 @@
use super::{
Pointer, EvalResult, AllocId, ScalarMaybeUndef, write_target_uint, read_target_uint, Scalar,
truncate,
};
use crate::ty::layout::{Size, Align};
@ -382,18 +381,9 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false),
};
let bytes = match val {
Scalar::Ptr(val) => {
assert_eq!(type_size, cx.data_layout().pointer_size);
val.offset.bytes() as u128
}
Scalar::Raw { data, size } => {
assert_eq!(size as u64, type_size.bytes());
debug_assert_eq!(truncate(data, Size::from_bytes(size.into())), data,
"Unexpected value of size {} when writing to memory", size);
data
},
let bytes = match val.to_bits_or_ptr(type_size, cx) {
Err(val) => val.offset.bytes() as u128,
Ok(data) => data,
};
let endian = cx.data_layout().endian;

View File

@ -232,7 +232,7 @@ impl<'tcx, Tag> Scalar<Tag> {
}
}
/// Returns this pointers offset from the allocation base, or from NULL (for
/// Returns this pointer's offset from the allocation base, or from NULL (for
/// integer pointers).
#[inline]
pub fn get_ptr_offset(self, cx: &impl HasDataLayout) -> Size {
@ -269,7 +269,7 @@ impl<'tcx, Tag> Scalar<Tag> {
#[inline]
pub fn from_uint(i: impl Into<u128>, size: Size) -> Self {
let i = i.into();
debug_assert_eq!(truncate(i, size), i,
assert_eq!(truncate(i, size), i,
"Unsigned value {} does not fit in {} bits", i, size.bits());
Scalar::Raw { data: i, size: size.bytes() as u8 }
}
@ -279,7 +279,7 @@ impl<'tcx, Tag> Scalar<Tag> {
let i = i.into();
// `into` performed sign extension, we have to truncate
let truncated = truncate(i as u128, size);
debug_assert_eq!(sign_extend(truncated, size) as i128, i,
assert_eq!(sign_extend(truncated, size) as i128, i,
"Signed value {} does not fit in {} bits", i, size.bits());
Scalar::Raw { data: truncated, size: size.bytes() as u8 }
}
@ -294,12 +294,35 @@ impl<'tcx, Tag> Scalar<Tag> {
Scalar::Raw { data: f.to_bits() as u128, size: 8 }
}
#[inline]
pub fn to_bits_or_ptr(
self,
target_size: Size,
cx: &impl HasDataLayout,
) -> Result<u128, Pointer<Tag>> {
match self {
Scalar::Raw { data, size } => {
assert_eq!(target_size.bytes(), size as u64);
assert_ne!(size, 0, "to_bits cannot be used with zsts");
assert_eq!(truncate(data, target_size), data,
"Scalar value {:#x} exceeds size of {} bytes", data, size);
Ok(data)
}
Scalar::Ptr(ptr) => {
assert_eq!(target_size, cx.data_layout().pointer_size);
Err(ptr)
}
}
}
#[inline]
pub fn to_bits(self, target_size: Size) -> EvalResult<'tcx, u128> {
match self {
Scalar::Raw { data, size } => {
assert_eq!(target_size.bytes(), size as u64);
assert_ne!(size, 0, "to_bits cannot be used with zsts");
assert_eq!(truncate(data, target_size), data,
"Scalar value {:#x} exceeds size of {} bytes", data, size);
Ok(data)
}
Scalar::Ptr(_) => err!(ReadPointerAsBytes),
@ -350,27 +373,23 @@ impl<'tcx, Tag> Scalar<Tag> {
pub fn to_u8(self) -> EvalResult<'static, u8> {
let sz = Size::from_bits(8);
let b = self.to_bits(sz)?;
assert_eq!(b as u8 as u128, b);
Ok(b as u8)
}
pub fn to_u32(self) -> EvalResult<'static, u32> {
let sz = Size::from_bits(32);
let b = self.to_bits(sz)?;
assert_eq!(b as u32 as u128, b);
Ok(b as u32)
}
pub fn to_u64(self) -> EvalResult<'static, u64> {
let sz = Size::from_bits(64);
let b = self.to_bits(sz)?;
assert_eq!(b as u64 as u128, b);
Ok(b as u64)
}
pub fn to_usize(self, cx: &impl HasDataLayout) -> EvalResult<'static, u64> {
let b = self.to_bits(cx.data_layout().pointer_size)?;
assert_eq!(b as u64 as u128, b);
Ok(b as u64)
}
@ -378,7 +397,6 @@ impl<'tcx, Tag> Scalar<Tag> {
let sz = Size::from_bits(8);
let b = self.to_bits(sz)?;
let b = sign_extend(b, sz) as i128;
assert_eq!(b as i8 as i128, b);
Ok(b as i8)
}
@ -386,7 +404,6 @@ impl<'tcx, Tag> Scalar<Tag> {
let sz = Size::from_bits(32);
let b = self.to_bits(sz)?;
let b = sign_extend(b, sz) as i128;
assert_eq!(b as i32 as i128, b);
Ok(b as i32)
}
@ -394,14 +411,13 @@ impl<'tcx, Tag> Scalar<Tag> {
let sz = Size::from_bits(64);
let b = self.to_bits(sz)?;
let b = sign_extend(b, sz) as i128;
assert_eq!(b as i64 as i128, b);
Ok(b as i64)
}
pub fn to_isize(self, cx: &impl HasDataLayout) -> EvalResult<'static, i64> {
let b = self.to_bits(cx.data_layout().pointer_size)?;
let b = sign_extend(b, cx.data_layout().pointer_size) as i128;
assert_eq!(b as i64 as i128, b);
let sz = cx.data_layout().pointer_size;
let b = self.to_bits(sz)?;
let b = sign_extend(b, sz) as i128;
Ok(b as i64)
}

View File

@ -443,7 +443,7 @@ impl Printer<'tcx, 'tcx> for SymbolPrinter<'_, 'tcx> {
ct: &'tcx ty::Const<'tcx>,
) -> Result<Self::Const, Self::Error> {
// only print integers
if let ConstValue::Scalar(Scalar::Bits { .. }) = ct.val {
if let ConstValue::Scalar(Scalar::Raw { .. }) = ct.val {
if ct.ty.is_integral() {
return self.pretty_print_const(ct);
}

View File

@ -101,6 +101,5 @@ fn parse_float<'tcx>(
}
};
// We trust that `data` is properly truncated.
Ok(ConstValue::Scalar(Scalar::Raw { data, size }))
Ok(ConstValue::Scalar(Scalar::from_uint(data, Size::from_bytes(size))))
}

View File

@ -6,7 +6,7 @@ use syntax::symbol::sym;
use rustc_apfloat::ieee::{Single, Double};
use rustc::mir::interpret::{
Scalar, EvalResult, Pointer, PointerArithmetic, InterpError, truncate
Scalar, EvalResult, Pointer, PointerArithmetic, InterpError,
};
use rustc::mir::CastKind;
use rustc_apfloat::Float;
@ -135,29 +135,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
use rustc::ty::TyKind::*;
trace!("Casting {:?}: {:?} to {:?}", val, src_layout.ty, dest_layout.ty);
match val {
Scalar::Ptr(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
Scalar::Raw { data, size } => {
debug_assert_eq!(size as u64, src_layout.size.bytes());
debug_assert_eq!(truncate(data, Size::from_bytes(size.into())), data,
"Unexpected value of size {} before casting", size);
let res = match src_layout.ty.sty {
Float(fty) => self.cast_from_float(data, fty, dest_layout.ty)?,
_ => self.cast_from_int(data, src_layout, dest_layout)?,
};
// Sanity check
match res {
Scalar::Ptr(_) => bug!("Fabricated a ptr value from an int...?"),
Scalar::Raw { data, size } => {
debug_assert_eq!(size as u64, dest_layout.size.bytes());
debug_assert_eq!(truncate(data, Size::from_bytes(size.into())), data,
"Unexpected value of size {} after casting", size);
}
match val.to_bits_or_ptr(src_layout.size, self) {
Err(ptr) => self.cast_from_ptr(ptr, dest_layout.ty),
Ok(data) => {
match src_layout.ty.sty {
Float(fty) => self.cast_from_float(data, fty, dest_layout.ty),
_ => self.cast_from_int(data, src_layout, dest_layout),
}
// Done
Ok(res)
}
}
}
@ -177,7 +161,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
trace!("cast_from_int: {}, {}, {}", v, src_layout.ty, dest_layout.ty);
use rustc::ty::TyKind::*;
match dest_layout.ty.sty {
Int(_) | Uint(_) => {
Int(_) | Uint(_) | RawPtr(_) => {
let v = self.truncate(v, dest_layout);
Ok(Scalar::from_uint(v, dest_layout.size))
}
@ -205,15 +189,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
Ok(Scalar::from_uint(v, Size::from_bytes(4)))
},
// No alignment check needed for raw pointers.
// But we have to truncate to target ptr size.
RawPtr(_) => {
Ok(Scalar::from_uint(
self.truncate_to_ptr(v).0,
self.pointer_size(),
))
},
// Casts to bool are not permitted by rustc, no need to handle them here.
_ => err!(Unimplemented(format!("int to {:?} cast", dest_layout.ty))),
}

View File

@ -247,16 +247,14 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
required_align: Align
) -> EvalResult<'tcx> {
// Check non-NULL/Undef, extract offset
let (offset, alloc_align) = match ptr {
Scalar::Ptr(ptr) => {
let (offset, alloc_align) = match ptr.to_bits_or_ptr(self.pointer_size(), self) {
Err(ptr) => {
// check this is not NULL -- which we can ensure only if this is in-bounds
// of some (potentially dead) allocation.
let align = self.check_bounds_ptr(ptr, InboundsCheck::MaybeDead)?;
(ptr.offset.bytes(), align)
}
Scalar::Raw { data, size } => {
assert_eq!(size as u64, self.pointer_size().bytes());
assert!(data < (1u128 << self.pointer_size().bits()));
Ok(data) => {
// check this is not NULL
if data == 0 {
return err!(InvalidNullPointerUsage);

View File

@ -639,18 +639,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
} => {
let variants_start = niche_variants.start().as_u32() as u128;
let variants_end = niche_variants.end().as_u32() as u128;
match raw_discr {
ScalarMaybeUndef::Scalar(Scalar::Ptr(ptr)) => {
let raw_discr = raw_discr.not_undef()
.map_err(|_| InterpError::InvalidDiscriminant(ScalarMaybeUndef::Undef))?;
match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) {
Err(ptr) => {
// The niche must be just 0 (which an inbounds pointer value never is)
let ptr_valid = niche_start == 0 && variants_start == variants_end &&
self.memory.check_bounds_ptr(ptr, InboundsCheck::MaybeDead).is_ok();
if !ptr_valid {
return err!(InvalidDiscriminant(raw_discr.erase_tag()));
return err!(InvalidDiscriminant(raw_discr.erase_tag().into()));
}
(dataful_variant.as_u32() as u128, dataful_variant)
},
ScalarMaybeUndef::Scalar(Scalar::Raw { data: raw_discr, size }) => {
assert_eq!(size as u64, discr_val.layout.size.bytes());
Ok(raw_discr) => {
let adjusted_discr = raw_discr.wrapping_sub(niche_start)
.wrapping_add(variants_start);
if variants_start <= adjusted_discr && adjusted_discr <= variants_end {
@ -665,8 +666,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M>
(dataful_variant.as_u32() as u128, dataful_variant)
}
},
ScalarMaybeUndef::Undef =>
return err!(InvalidDiscriminant(ScalarMaybeUndef::Undef)),
}
}
})

View File

@ -480,8 +480,8 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
wrapping_range_format(&layout.valid_range, max_hi),
)
);
let bits = match value {
Scalar::Ptr(ptr) => {
let bits = match value.to_bits_or_ptr(op.layout.size, self.ecx) {
Err(ptr) => {
if lo == 1 && hi == max_hi {
// only NULL is not allowed.
// We can call `check_align` to check non-NULL-ness, but have to also look
@ -509,10 +509,8 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
);
}
}
Scalar::Raw { data, size } => {
assert_eq!(size as u64, op.layout.size.bytes());
Ok(data) =>
data
}
};
// Now compare. This is slightly subtle because this is a special "wrap-around" range.
if wrapping_range_contains(&layout.valid_range, bits) {