use crate::ty::layout::{Size, Align};
use syntax::ast::Mutability;
-use std::iter;
+use std::{iter, fmt::{self, Display}};
use crate::mir;
use std::ops::{Deref, DerefMut};
use rustc_data_structures::sorted_map::SortedMap;
MaybeDead,
}
+/// Used by `check_in_alloc` to indicate whether the pointer needs to be just inbounds
+/// or also inbounds of a *live* allocation.
+#[derive(Debug, Copy, Clone, RustcEncodable, RustcDecodable, HashStable)]
+pub enum CheckInAllocMsg {
+ ReadCStr,
+ CheckBytes,
+ WriteBytes,
+ WriteRepeat,
+ ReadScalar,
+ WriteScalar,
+ SlicePatCoveredByConst,
+ ReadDiscriminant,
+ CheckAlign,
+ ReadBytes,
+ CopyRepeatedly,
+ CheckBounds,
+}
+
+impl Display for CheckInAllocMsg {
+
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", match *self {
+ CheckInAllocMsg::ReadCStr => "read C str",
+ CheckInAllocMsg::CheckBytes => "check bytes",
+ CheckInAllocMsg::WriteBytes => "write bytes",
+ CheckInAllocMsg::WriteRepeat => "write repeat",
+ CheckInAllocMsg::ReadScalar => "read scalar",
+ CheckInAllocMsg::WriteScalar => "write scalar",
+ CheckInAllocMsg::SlicePatCoveredByConst => "slice pat covered by const",
+ CheckInAllocMsg::ReadDiscriminant => "read discriminant",
+ CheckInAllocMsg::CheckAlign => "check align",
+ CheckInAllocMsg::ReadBytes => "read bytes",
+ CheckInAllocMsg::CopyRepeatedly => "copy repeatedly",
+ CheckInAllocMsg::CheckBounds => "check bounds",
+ })
+ }
+}
+
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
pub struct Allocation<Tag=(),Extra=()> {
/// The actual bytes of the allocation.
fn check_bounds_ptr(
&self,
ptr: Pointer<Tag>,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx> {
let allocation_size = self.bytes.len() as u64;
- ptr.check_in_alloc(Size::from_bytes(allocation_size), InboundsCheck::Live)
+ ptr.check_in_alloc(Size::from_bytes(allocation_size), msg)
}
/// Checks if the memory range beginning at `ptr` and of size `Size` is "in-bounds".
cx: &impl HasDataLayout,
ptr: Pointer<Tag>,
size: Size,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx> {
// if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
- self.check_bounds_ptr(ptr.offset(size, cx)?)
+ self.check_bounds_ptr(ptr.offset(size, cx)?, msg)
}
}
ptr: Pointer<Tag>,
size: Size,
check_defined_and_ptr: bool,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx, &[u8]>
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
where Extra: AllocationExtra<Tag, MemoryExtra>
{
- self.check_bounds(cx, ptr, size)?;
+ self.check_bounds(cx, ptr, size, msg)?;
if check_defined_and_ptr {
self.check_defined(ptr, size)?;
cx: &impl HasDataLayout,
ptr: Pointer<Tag>,
size: Size,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx, &[u8]>
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
where Extra: AllocationExtra<Tag, MemoryExtra>
{
- self.get_bytes_internal(cx, ptr, size, true)
+ self.get_bytes_internal(cx, ptr, size, true, msg)
}
/// It is the caller's responsibility to handle undefined and pointer bytes.
cx: &impl HasDataLayout,
ptr: Pointer<Tag>,
size: Size,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx, &[u8]>
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
where Extra: AllocationExtra<Tag, MemoryExtra>
{
- self.get_bytes_internal(cx, ptr, size, false)
+ self.get_bytes_internal(cx, ptr, size, false, msg)
}
/// Just calling this already marks everything as defined and removes relocations,
cx: &impl HasDataLayout,
ptr: Pointer<Tag>,
size: Size,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx, &mut [u8]>
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
where Extra: AllocationExtra<Tag, MemoryExtra>
{
assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
- self.check_bounds(cx, ptr, size)?;
+ self.check_bounds(cx, ptr, size, msg)?;
self.mark_definedness(ptr, size, true)?;
self.clear_relocations(cx, ptr, size)?;
// Go through `get_bytes` for checks and AllocationExtra hooks.
// We read the null, so we include it in the request, but we want it removed
// from the result!
- Ok(&self.get_bytes(cx, ptr, size_with_null)?[..size])
+ Ok(&self.get_bytes(cx, ptr, size_with_null, CheckInAllocMsg::ReadCStr)?[..size])
}
None => err!(UnterminatedCString(ptr.erase_tag())),
}
where Extra: AllocationExtra<Tag, MemoryExtra>
{
// Check bounds and relocations on the edges
- self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
+ self.get_bytes_with_undef_and_ptr(cx, ptr, size, CheckInAllocMsg::CheckBytes)?;
// Check undef and ptr
if !allow_ptr_and_undef {
self.check_defined(ptr, size)?;
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
where Extra: AllocationExtra<Tag, MemoryExtra>
{
- let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64))?;
+ let bytes = self.get_bytes_mut(cx, ptr, Size::from_bytes(src.len() as u64), CheckInAllocMsg::WriteBytes)?;
bytes.clone_from_slice(src);
Ok(())
}
// FIXME: Working around https://github.com/rust-lang/rust/issues/56209
where Extra: AllocationExtra<Tag, MemoryExtra>
{
- let bytes = self.get_bytes_mut(cx, ptr, count)?;
+ let bytes = self.get_bytes_mut(cx, ptr, count, CheckInAllocMsg::WriteRepeat)?;
for b in bytes {
*b = val;
}
where Extra: AllocationExtra<Tag, MemoryExtra>
{
// get_bytes_unchecked tests relocation edges
- let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size)?;
+ let bytes = self.get_bytes_with_undef_and_ptr(cx, ptr, size, CheckInAllocMsg::ReadScalar)?;
// Undef check happens *after* we established that the alignment is correct.
// We must not return Ok() for unaligned pointers!
if self.check_defined(ptr, size).is_err() {
};
let endian = cx.data_layout().endian;
- let dst = self.get_bytes_mut(cx, ptr, type_size)?;
+ let dst = self.get_bytes_mut(cx, ptr, type_size, CheckInAllocMsg::WriteScalar)?;
write_target_uint(endian, dst, bytes).unwrap();
// See if we have to also write a relocation
use rustc_target::spec::abi::Abi;
use rustc_macros::HashStable;
-use super::{RawConst, Pointer, InboundsCheck, ScalarMaybeUndef};
+use super::{RawConst, Pointer, CheckInAllocMsg, ScalarMaybeUndef};
use backtrace::Backtrace;
InvalidDiscriminant(ScalarMaybeUndef),
PointerOutOfBounds {
ptr: Pointer,
- check: InboundsCheck,
+ msg: CheckInAllocMsg,
allocation_size: Size,
},
InvalidNullPointerUsage,
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::EvalErrorKind::*;
match *self {
- PointerOutOfBounds { ptr, check, allocation_size } => {
+ PointerOutOfBounds { ptr, msg, allocation_size } => {
write!(f, "Pointer must be in-bounds{} at offset {}, but is outside bounds of \
- allocation {} which has size {}",
- match check {
- InboundsCheck::Live => " and live",
- InboundsCheck::MaybeDead => "",
- },
+ allocation {} which has size {}", msg,
ptr.offset.bytes(), ptr.alloc_id, allocation_size.bytes())
},
ValidationFailure(ref err) => {
pub use self::allocation::{
InboundsCheck, Allocation, AllocationExtra,
- Relocations, UndefMask,
+ Relocations, UndefMask, CheckInAllocMsg,
};
pub use self::pointer::{Pointer, PointerArithmetic};
use rustc_macros::HashStable;
use super::{
- AllocId, EvalResult, InboundsCheck,
+ AllocId, EvalResult, CheckInAllocMsg
};
////////////////////////////////////////////////////////////////////////////////
pub fn check_in_alloc(
self,
allocation_size: Size,
- check: InboundsCheck,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx, ()> {
if self.offset > allocation_size {
err!(PointerOutOfBounds {
ptr: self.erase_tag(),
- check,
+ msg,
allocation_size,
})
} else {
use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size};
use rustc::mir::Field;
-use rustc::mir::interpret::{ConstValue, Scalar, truncate};
+use rustc::mir::interpret::{ConstValue, Scalar, truncate, CheckInAllocMsg};
use rustc::util::common::ErrorReported;
use syntax::attr::{SignedInt, UnsignedInt};
return Ok(false);
}
let n = n.assert_usize(tcx).unwrap();
- alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
+ alloc.get_bytes(&tcx, ptr, Size::from_bytes(n), CheckInAllocMsg::SlicePatCoveredByConst).unwrap()
},
// a slice fat pointer to a zero length slice
(ConstValue::Slice(Scalar::Bits { .. }, 0), ty::Slice(t)) => {
tcx.alloc_map
.lock()
.unwrap_memory(ptr.alloc_id)
- .get_bytes(&tcx, ptr, Size::from_bytes(n))
+ .get_bytes(&tcx, ptr, Size::from_bytes(n), CheckInAllocMsg::SlicePatCoveredByConst)
.unwrap()
},
_ => bug!(
use super::{
Pointer, AllocId, Allocation, GlobalId, AllocationExtra,
EvalResult, Scalar, EvalErrorKind, AllocKind, PointerArithmetic,
- Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck,
+ Machine, AllocMap, MayLeak, ErrorHandled, InboundsCheck, CheckInAllocMsg,
};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
Scalar::Ptr(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)?;
+ let align = self.check_bounds_ptr(ptr, CheckInAllocMsg::CheckAlign)?;
(ptr.offset.bytes(), align)
}
Scalar::Bits { bits, size } => {
pub fn check_bounds_ptr(
&self,
ptr: Pointer<M::PointerTag>,
- liveness: InboundsCheck,
+ msg: CheckInAllocMsg,
) -> EvalResult<'tcx, Align> {
- let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, liveness)?;
- ptr.check_in_alloc(allocation_size, liveness)?;
+ let (allocation_size, align) = self.get_size_and_align(ptr.alloc_id, msg)?;
+ ptr.check_in_alloc(allocation_size, msg)?;
Ok(align)
}
}
pub fn get_size_and_align(
&self,
id: AllocId,
- liveness: InboundsCheck,
+ msg: CheckInAllocMsg,
) -> EvalResult<'static, (Size, Align)> {
if let Ok(alloc) = self.get(id) {
return Ok((Size::from_bytes(alloc.bytes.len() as u64), alloc.align));
let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
Ok((layout.size, layout.align.abi))
}
- _ => match liveness {
+ _ => match msg {
InboundsCheck::MaybeDead => {
// Must be a deallocated pointer
Ok(*self.dead_alloc_map.get(&id).expect(
Ok(&[])
} else {
let ptr = ptr.to_ptr()?;
- self.get(ptr.alloc_id)?.get_bytes(self, ptr, size)
+ self.get(ptr.alloc_id)?.get_bytes(self, ptr, size, CheckInAllocMsg::ReadBytes)
}
}
}
// This checks relocation edges on the src.
let src_bytes = self.get(src.alloc_id)?
- .get_bytes_with_undef_and_ptr(&tcx, src, size)?
+ .get_bytes_with_undef_and_ptr(&tcx, src, size, CheckInAllocMsg::CopyRepeatedly)?
.as_ptr();
let dest_bytes = self.get_mut(dest.alloc_id)?
- .get_bytes_mut(&tcx, dest, size * length)?
+ .get_bytes_mut(&tcx, dest, size * length, CheckInAllocMsg::CopyRepeatedly)?
.as_mut_ptr();
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt, VariantIdx};
use rustc::mir::interpret::{
- GlobalId, AllocId, InboundsCheck,
+ GlobalId, AllocId, CheckInAllocMsg,
ConstValue, Pointer, Scalar,
EvalResult, EvalErrorKind,
sign_extend, truncate,
ScalarMaybeUndef::Scalar(Scalar::Ptr(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();
+ self.memory.check_bounds_ptr(ptr, CheckInAllocMsg::ReadDiscriminant).is_ok();
if !ptr_valid {
return err!(InvalidDiscriminant(raw_discr.erase_tag()));
}
use rustc::ty;
use rustc_data_structures::fx::FxHashSet;
use rustc::mir::interpret::{
- Scalar, AllocKind, EvalResult, EvalErrorKind,
+ Scalar, AllocKind, EvalResult, EvalErrorKind, CheckInAllocMsg,
};
use super::{
try_validation!(
self.ecx.memory
.get(ptr.alloc_id)?
- .check_bounds(self.ecx, ptr, size),
+ .check_bounds(self.ecx, ptr, size, CheckInAllocMsg::CheckBounds),
"dangling (not entirely in bounds) reference", self.path);
}
// Check if we have encountered this pointer+layout combination