const validation: point at where we found a pointer but expected an integer
This commit is contained in:
parent
7637653b9f
commit
7767cbb3b0
34 changed files with 544 additions and 408 deletions
|
@ -18,9 +18,9 @@ use rustc_span::DUMMY_SP;
|
|||
use rustc_target::abi::{Align, HasDataLayout, Size};
|
||||
|
||||
use super::{
|
||||
read_target_uint, write_target_uint, AllocId, InterpError, InterpResult, Pointer, Provenance,
|
||||
ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess,
|
||||
UnsupportedOpInfo,
|
||||
read_target_uint, write_target_uint, AllocId, BadBytesAccess, InterpError, InterpResult,
|
||||
Pointer, PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch,
|
||||
UndefinedBehaviorInfo, UnsupportedOpInfo,
|
||||
};
|
||||
use crate::ty;
|
||||
use init_mask::*;
|
||||
|
@ -173,13 +173,13 @@ pub enum AllocError {
|
|||
/// A scalar had the wrong size.
|
||||
ScalarSizeMismatch(ScalarSizeMismatch),
|
||||
/// Encountered a pointer where we needed raw bytes.
|
||||
ReadPointerAsBytes,
|
||||
ReadPointerAsInt(Option<BadBytesAccess>),
|
||||
/// Partially overwriting a pointer.
|
||||
PartialPointerOverwrite(Size),
|
||||
OverwritePartialPointer(Size),
|
||||
/// Partially copying a pointer.
|
||||
PartialPointerCopy(Size),
|
||||
ReadPartialPointer(Size),
|
||||
/// Using uninitialized data where it is not allowed.
|
||||
InvalidUninitBytes(Option<UninitBytesAccess>),
|
||||
InvalidUninitBytes(Option<BadBytesAccess>),
|
||||
}
|
||||
pub type AllocResult<T = ()> = Result<T, AllocError>;
|
||||
|
||||
|
@ -196,12 +196,14 @@ impl AllocError {
|
|||
ScalarSizeMismatch(s) => {
|
||||
InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ScalarSizeMismatch(s))
|
||||
}
|
||||
ReadPointerAsBytes => InterpError::Unsupported(UnsupportedOpInfo::ReadPointerAsBytes),
|
||||
PartialPointerOverwrite(offset) => InterpError::Unsupported(
|
||||
UnsupportedOpInfo::PartialPointerOverwrite(Pointer::new(alloc_id, offset)),
|
||||
ReadPointerAsInt(info) => InterpError::Unsupported(
|
||||
UnsupportedOpInfo::ReadPointerAsInt(info.map(|b| (alloc_id, b))),
|
||||
),
|
||||
PartialPointerCopy(offset) => InterpError::Unsupported(
|
||||
UnsupportedOpInfo::PartialPointerCopy(Pointer::new(alloc_id, offset)),
|
||||
OverwritePartialPointer(offset) => InterpError::Unsupported(
|
||||
UnsupportedOpInfo::OverwritePartialPointer(Pointer::new(alloc_id, offset)),
|
||||
),
|
||||
ReadPartialPointer(offset) => InterpError::Unsupported(
|
||||
UnsupportedOpInfo::ReadPartialPointer(Pointer::new(alloc_id, offset)),
|
||||
),
|
||||
InvalidUninitBytes(info) => InterpError::UndefinedBehavior(
|
||||
UndefinedBehaviorInfo::InvalidUninitBytes(info.map(|b| (alloc_id, b))),
|
||||
|
@ -433,14 +435,26 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
|
|||
range: AllocRange,
|
||||
) -> AllocResult<&[u8]> {
|
||||
self.init_mask.is_range_initialized(range).map_err(|uninit_range| {
|
||||
AllocError::InvalidUninitBytes(Some(UninitBytesAccess {
|
||||
AllocError::InvalidUninitBytes(Some(BadBytesAccess {
|
||||
access: range,
|
||||
uninit: uninit_range,
|
||||
bad: uninit_range,
|
||||
}))
|
||||
})?;
|
||||
if !Prov::OFFSET_IS_ADDR {
|
||||
if !self.provenance.range_empty(range, cx) {
|
||||
return Err(AllocError::ReadPointerAsBytes);
|
||||
// Find the provenance.
|
||||
let (offset, _prov) = self
|
||||
.provenance
|
||||
.range_get_ptrs(range, cx)
|
||||
.first()
|
||||
.copied()
|
||||
.expect("there must be provenance somewhere here");
|
||||
let start = offset.max(range.start); // the pointer might begin before `range`!
|
||||
let end = (offset + cx.pointer_size()).min(range.end()); // the pointer might end after `range`!
|
||||
return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess {
|
||||
access: range,
|
||||
bad: AllocRange::from(start..end),
|
||||
})));
|
||||
}
|
||||
}
|
||||
Ok(self.get_bytes_unchecked(range))
|
||||
|
@ -536,23 +550,25 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
|
|||
// Now use this provenance.
|
||||
let ptr = Pointer::new(prov, Size::from_bytes(bits));
|
||||
return Ok(Scalar::from_maybe_pointer(ptr, cx));
|
||||
} else {
|
||||
// Without OFFSET_IS_ADDR, the only remaining case we can handle is total absence of
|
||||
// provenance.
|
||||
if self.provenance.range_empty(range, cx) {
|
||||
return Ok(Scalar::from_uint(bits, range.size));
|
||||
}
|
||||
// Else we have mixed provenance, that doesn't work.
|
||||
return Err(AllocError::ReadPartialPointer(range.start));
|
||||
}
|
||||
} else {
|
||||
// We are *not* reading a pointer.
|
||||
// If we can just ignore provenance, do exactly that.
|
||||
if Prov::OFFSET_IS_ADDR {
|
||||
// If we can just ignore provenance or there is none, that's easy.
|
||||
if Prov::OFFSET_IS_ADDR || self.provenance.range_empty(range, cx) {
|
||||
// We just strip provenance.
|
||||
return Ok(Scalar::from_uint(bits, range.size));
|
||||
}
|
||||
// There is some provenance and we don't have OFFSET_IS_ADDR. This doesn't work.
|
||||
return Err(AllocError::ReadPointerAsInt(None));
|
||||
}
|
||||
|
||||
// Fallback path for when we cannot treat provenance bytewise or ignore it.
|
||||
assert!(!Prov::OFFSET_IS_ADDR);
|
||||
if !self.provenance.range_empty(range, cx) {
|
||||
return Err(AllocError::ReadPointerAsBytes);
|
||||
}
|
||||
// There is no provenance, we can just return the bits.
|
||||
Ok(Scalar::from_uint(bits, range.size))
|
||||
}
|
||||
|
||||
/// Writes a *non-ZST* scalar.
|
||||
|
|
|
@ -66,7 +66,11 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
/// Returns all ptr-sized provenance in the given range.
|
||||
/// If the range has length 0, returns provenance that crosses the edge between `start-1` and
|
||||
/// `start`.
|
||||
fn range_get_ptrs(&self, range: AllocRange, cx: &impl HasDataLayout) -> &[(Size, Prov)] {
|
||||
pub(super) fn range_get_ptrs(
|
||||
&self,
|
||||
range: AllocRange,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> &[(Size, Prov)] {
|
||||
// We have to go back `pointer_size - 1` bytes, as that one would still overlap with
|
||||
// the beginning of this range.
|
||||
let adjusted_start = Size::from_bytes(
|
||||
|
@ -158,7 +162,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
if first < start {
|
||||
if !Prov::OFFSET_IS_ADDR {
|
||||
// We can't split up the provenance into less than a pointer.
|
||||
return Err(AllocError::PartialPointerOverwrite(first));
|
||||
return Err(AllocError::OverwritePartialPointer(first));
|
||||
}
|
||||
// Insert the remaining part in the bytewise provenance.
|
||||
let prov = self.ptrs[&first];
|
||||
|
@ -171,7 +175,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
let begin_of_last = last - cx.data_layout().pointer_size;
|
||||
if !Prov::OFFSET_IS_ADDR {
|
||||
// We can't split up the provenance into less than a pointer.
|
||||
return Err(AllocError::PartialPointerOverwrite(begin_of_last));
|
||||
return Err(AllocError::OverwritePartialPointer(begin_of_last));
|
||||
}
|
||||
// Insert the remaining part in the bytewise provenance.
|
||||
let prov = self.ptrs[&begin_of_last];
|
||||
|
@ -246,10 +250,10 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
|
|||
if !Prov::OFFSET_IS_ADDR {
|
||||
// There can't be any bytewise provenance, and we cannot split up the begin/end overlap.
|
||||
if let Some(entry) = begin_overlap {
|
||||
return Err(AllocError::PartialPointerCopy(entry.0));
|
||||
return Err(AllocError::ReadPartialPointer(entry.0));
|
||||
}
|
||||
if let Some(entry) = end_overlap {
|
||||
return Err(AllocError::PartialPointerCopy(entry.0));
|
||||
return Err(AllocError::ReadPartialPointer(entry.0));
|
||||
}
|
||||
debug_assert!(self.bytes.is_none());
|
||||
} else {
|
||||
|
|
|
@ -134,10 +134,6 @@ impl InterpErrorBacktrace {
|
|||
}
|
||||
|
||||
impl<'tcx> InterpErrorInfo<'tcx> {
|
||||
pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
|
||||
Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
|
||||
}
|
||||
|
||||
pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
|
||||
let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
|
||||
(kind, backtrace)
|
||||
|
@ -226,13 +222,13 @@ impl IntoDiagnosticArg for InvalidMetaKind {
|
|||
}
|
||||
}
|
||||
|
||||
/// Details of an access to uninitialized bytes where it is not allowed.
|
||||
/// Details of an access to uninitialized bytes / bad pointer bytes where it is not allowed.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct UninitBytesAccess {
|
||||
pub struct BadBytesAccess {
|
||||
/// Range of the original memory access.
|
||||
pub access: AllocRange,
|
||||
/// Range of the uninit memory that was encountered. (Might not be maximal.)
|
||||
pub uninit: AllocRange,
|
||||
/// Range of the bad memory that was encountered. (Might not be maximal.)
|
||||
pub bad: AllocRange,
|
||||
}
|
||||
|
||||
/// Information about a size mismatch.
|
||||
|
@ -316,7 +312,7 @@ pub enum UndefinedBehaviorInfo<'a> {
|
|||
/// Using a string that is not valid UTF-8,
|
||||
InvalidStr(std::str::Utf8Error),
|
||||
/// Using uninitialized data where it is not allowed.
|
||||
InvalidUninitBytes(Option<(AllocId, UninitBytesAccess)>),
|
||||
InvalidUninitBytes(Option<(AllocId, BadBytesAccess)>),
|
||||
/// Working with a local that is not currently live.
|
||||
DeadLocal,
|
||||
/// Data size is not equal to target size.
|
||||
|
@ -326,7 +322,7 @@ pub enum UndefinedBehaviorInfo<'a> {
|
|||
/// An uninhabited enum variant is projected.
|
||||
UninhabitedEnumVariantRead(VariantIdx),
|
||||
/// Validation error.
|
||||
Validation(ValidationErrorInfo<'a>),
|
||||
ValidationError(ValidationErrorInfo<'a>),
|
||||
// FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
|
||||
// dispatched
|
||||
/// A custom (free-form) error, created by `err_ub_custom!`.
|
||||
|
@ -368,6 +364,8 @@ pub enum ExpectedKind {
|
|||
Float,
|
||||
Int,
|
||||
FnPtr,
|
||||
EnumTag,
|
||||
Str,
|
||||
}
|
||||
|
||||
impl From<PointerKind> for ExpectedKind {
|
||||
|
@ -381,10 +379,11 @@ impl From<PointerKind> for ExpectedKind {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub enum ValidationErrorKind<'tcx> {
|
||||
PointerAsInt { expected: ExpectedKind },
|
||||
PartialPointer,
|
||||
PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
|
||||
PtrToStatic { ptr_kind: PointerKind },
|
||||
PtrToMut { ptr_kind: PointerKind },
|
||||
ExpectedNonPtr { value: String },
|
||||
MutableRefInConst,
|
||||
NullFnPtr,
|
||||
NeverVal,
|
||||
|
@ -394,11 +393,8 @@ pub enum ValidationErrorKind<'tcx> {
|
|||
UnsafeCell,
|
||||
UninhabitedVal { ty: Ty<'tcx> },
|
||||
InvalidEnumTag { value: String },
|
||||
UninhabitedEnumTag,
|
||||
UninitEnumTag,
|
||||
UninitStr,
|
||||
UninhabitedEnumVariant,
|
||||
Uninit { expected: ExpectedKind },
|
||||
UninitVal,
|
||||
InvalidVTablePtr { value: String },
|
||||
InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
|
||||
InvalidMetaTooLarge { ptr_kind: PointerKind },
|
||||
|
@ -426,12 +422,12 @@ pub enum UnsupportedOpInfo {
|
|||
//
|
||||
/// Overwriting parts of a pointer; without knowing absolute addresses, the resulting state
|
||||
/// cannot be represented by the CTFE interpreter.
|
||||
PartialPointerOverwrite(Pointer<AllocId>),
|
||||
/// Attempting to `copy` parts of a pointer to somewhere else; without knowing absolute
|
||||
OverwritePartialPointer(Pointer<AllocId>),
|
||||
/// Attempting to read or copy parts of a pointer to somewhere else; without knowing absolute
|
||||
/// addresses, the resulting state cannot be represented by the CTFE interpreter.
|
||||
PartialPointerCopy(Pointer<AllocId>),
|
||||
/// Encountered a pointer where we needed raw bytes.
|
||||
ReadPointerAsBytes,
|
||||
ReadPartialPointer(Pointer<AllocId>),
|
||||
/// Encountered a pointer where we needed an integer.
|
||||
ReadPointerAsInt(Option<(AllocId, BadBytesAccess)>),
|
||||
/// Accessing thread local statics
|
||||
ThreadLocalStatic(DefId),
|
||||
/// Accessing an unsupported extern static.
|
||||
|
@ -497,7 +493,7 @@ impl InterpError<'_> {
|
|||
matches!(
|
||||
self,
|
||||
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
|
||||
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
|
||||
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationError { .. })
|
||||
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -142,11 +142,11 @@ use crate::ty::GenericArgKind;
|
|||
use crate::ty::{self, Instance, Ty, TyCtxt};
|
||||
|
||||
pub use self::error::{
|
||||
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
|
||||
EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
|
||||
InvalidProgramInfo, MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
|
||||
ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
|
||||
ValidationErrorInfo, ValidationErrorKind,
|
||||
struct_error, BadBytesAccess, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult,
|
||||
EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo,
|
||||
InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, PointerKind,
|
||||
ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
|
||||
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
|
||||
};
|
||||
|
||||
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
|
||||
|
|
|
@ -378,15 +378,16 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
|
|||
#[inline]
|
||||
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
|
||||
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
|
||||
self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsBytes))?.to_bits(target_size).map_err(
|
||||
|size| {
|
||||
self.try_to_int()
|
||||
.map_err(|_| err_unsup!(ReadPointerAsInt(None)))?
|
||||
.to_bits(target_size)
|
||||
.map_err(|size| {
|
||||
err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
|
||||
target_size: target_size.bytes(),
|
||||
data_size: size.bytes(),
|
||||
}))
|
||||
.into()
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue