From f5efb68a24b66a21b7eca055b9e8867eeaf61366 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 8 Mar 2020 18:52:30 +0100 Subject: [PATCH] miri: categorize errors into "unsupported" and "UB" Also slightly refactor pointer bounds checks to avoid creating unnecessary temporary Errors --- src/librustc/mir/interpret/allocation.rs | 4 +- src/librustc/mir/interpret/error.rs | 297 ++++++++------------- src/librustc/mir/interpret/mod.rs | 14 +- src/librustc/mir/interpret/pointer.rs | 16 -- src/librustc/mir/interpret/value.rs | 13 +- src/librustc_mir/const_eval/machine.rs | 3 +- src/librustc_mir/interpret/eval_context.rs | 6 +- src/librustc_mir/interpret/intern.rs | 4 +- src/librustc_mir/interpret/intrinsics.rs | 2 +- src/librustc_mir/interpret/machine.rs | 2 +- src/librustc_mir/interpret/memory.rs | 67 +++-- src/librustc_mir/interpret/operand.rs | 4 +- src/librustc_mir/interpret/place.rs | 2 +- src/librustc_mir/interpret/terminator.rs | 207 +++++++------- src/librustc_mir/interpret/validity.rs | 16 +- 15 files changed, 293 insertions(+), 364 deletions(-) diff --git a/src/librustc/mir/interpret/allocation.rs b/src/librustc/mir/interpret/allocation.rs index 546ba586d30f..48b26cfd83cd 100644 --- a/src/librustc/mir/interpret/allocation.rs +++ b/src/librustc/mir/interpret/allocation.rs @@ -314,7 +314,7 @@ impl<'tcx, Tag: Copy, Extra: AllocationExtra> Allocation { &self.get_bytes(cx, ptr, size_with_null)?[..size] } // This includes the case where `offset` is out-of-bounds to begin with. - None => throw_unsup!(UnterminatedCString(ptr.erase_tag())), + None => throw_ub!(UnterminatedCString(ptr.erase_tag())), }) } @@ -573,7 +573,7 @@ impl<'tcx, Tag, Extra> Allocation { fn check_defined(&self, ptr: Pointer, size: Size) -> InterpResult<'tcx> { self.undef_mask .is_range_defined(ptr.offset, ptr.offset + size) - .or_else(|idx| throw_unsup!(ReadUndefBytes(idx))) + .or_else(|idx| throw_ub!(InvalidUndefBytes(Some(Pointer::new(ptr.alloc_id, idx))))) } pub fn mark_definedness(&mut self, ptr: Pointer, size: Size, new_state: bool) { diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index 0b33408edf02..ff0e9f2771f4 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -1,7 +1,6 @@ -use super::{CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef}; +use super::{AllocId, CheckInAllocMsg, Pointer, RawConst, ScalarMaybeUndef}; use crate::hir::map::definitions::DefPathData; -use crate::mir; use crate::mir::interpret::ConstValue; use crate::ty::layout::{Align, LayoutError, Size}; use crate::ty::query::TyCtxtAt; @@ -14,9 +13,8 @@ use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_macros::HashStable; use rustc_session::CtfeBacktrace; -use rustc_span::{Pos, Span}; -use rustc_target::spec::abi::Abi; -use std::{any::Any, fmt}; +use rustc_span::{Pos, Span, def_id::DefId}; +use std::{any::Any, env, fmt}; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)] pub enum ErrorHandled { @@ -296,6 +294,8 @@ pub enum InvalidProgramInfo<'tcx> { TypeckError, /// An error occurred during layout computation. Layout(layout::LayoutError<'tcx>), + /// An invalid transmute happened. + TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), } impl fmt::Debug for InvalidProgramInfo<'_> { @@ -306,6 +306,11 @@ impl fmt::Debug for InvalidProgramInfo<'_> { ReferencedConstant => write!(f, "referenced constant has errors"), TypeckError => write!(f, "encountered constants with type errors, stopping evaluation"), Layout(ref err) => write!(f, "{}", err), + TransmuteSizeDiff(from_ty, to_ty) => write!( + f, + "tried to transmute from {:?} to {:?}, but their sizes differed", + from_ty, to_ty + ), } } } @@ -330,6 +335,43 @@ pub enum UndefinedBehaviorInfo { PointerArithOverflow, /// Invalid metadata in a wide pointer (using `str` to avoid allocations). InvalidMeta(&'static str), + /// Reading a C string that does not end within its allocation. + UnterminatedCString(Pointer), + /// Dereferencing a dangling pointer after it got freed. + PointerUseAfterFree(AllocId), + /// Using a NULL pointer in the wrong way. + InvalidNullPointerUsage, + /// Used a pointer outside the bounds it is valid for. + PointerOutOfBounds { + ptr: Pointer, + msg: CheckInAllocMsg, + allocation_size: Size, + }, + /// Used a pointer with bad alignment. + AlignmentCheckFailed { + required: Align, + has: Align, + }, + /// Writing to read-only memory. + WriteToReadOnly(AllocId), + /// Using a pointer-not-to-a-function as function pointer. + InvalidFunctionPointer(Pointer), + // Trying to access the data behind a function pointer. + DerefFunctionPointer(AllocId), + /// The value validity check found a problem. + /// Should only be thrown by `validity.rs` and always point out which part of the value + /// is the problem. + ValidationFailure(String), + /// Using a non-boolean `u8` as bool. + InvalidBool(u8), + /// Using a non-character `u32` as character. + InvalidChar(u32), + /// Using uninitialized data where it is not allowed. + InvalidUndefBytes(Option), + /// Working with a local that is not currently live. + DeadLocal, + /// Trying to read from the return place of a function. + ReadFromReturnPlace, } impl fmt::Debug for UndefinedBehaviorInfo { @@ -348,6 +390,44 @@ impl fmt::Debug for UndefinedBehaviorInfo { RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), + UnterminatedCString(p) => write!( + f, + "reading a null-terminated string starting at {:?} with no null found before end of allocation", + p, + ), + PointerUseAfterFree(a) => write!( + f, + "pointer to allocation {:?} was dereferenced after allocation got freed", + a + ), + InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"), + PointerOutOfBounds { ptr, msg, allocation_size } => write!( + f, + "{} failed: pointer must be in-bounds at offset {}, \ + but is outside bounds of allocation {} which has size {}", + msg, + ptr.offset.bytes(), + ptr.alloc_id, + allocation_size.bytes() + ), + AlignmentCheckFailed { required, has } => write!( + f, + "accessing memory with alignment {}, but alignment {} is required", + has.bytes(), + required.bytes() + ), + WriteToReadOnly(a) => write!(f, "writing to read-only allocation {:?}", a), + InvalidFunctionPointer(p) => { + write!(f, "using {:?} as function pointer but it does not point to a function", p) + } + DerefFunctionPointer(a) => write!(f, "accessing data behind function pointer allocation {:?}", a), + ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), + InvalidBool(b) => write!(f, "interpreting an invalid 8-bit value as a bool: {}", b), + InvalidChar(c) => write!(f, "interpreting an invalid 32-bit value as a char: {}", c), + InvalidUndefBytes(Some(p)) => write!(f, "reading uninitialized memory at {:?}, but this operation requires initialized memory", p), + InvalidUndefBytes(None) => write!(f, "using uninitialized data, but this operation requires initialized memory"), + DeadLocal => write!(f, "accessing a dead local variable"), + ReadFromReturnPlace => write!(f, "tried to read from the return place"), } } } @@ -359,7 +439,7 @@ impl fmt::Debug for UndefinedBehaviorInfo { /// /// Currently, we also use this as fall-back error kind for errors that have not been /// categorized yet. -pub enum UnsupportedOpInfo<'tcx> { +pub enum UnsupportedOpInfo { /// Free-form case. Only for errors that are never caught! Unsupported(String), @@ -367,194 +447,45 @@ pub enum UnsupportedOpInfo<'tcx> { /// This must not allocate for performance reasons (hence `str`, not `String`). ConstPropUnsupported(&'static str), - // -- Everything below is not categorized yet -- - FunctionAbiMismatch(Abi, Abi), - FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>), - FunctionRetMismatch(Ty<'tcx>, Ty<'tcx>), - FunctionArgCountMismatch, - UnterminatedCString(Pointer), - DanglingPointerDeref, - DoubleFree, - InvalidMemoryAccess, - InvalidFunctionPointer, - InvalidBool, - PointerOutOfBounds { - ptr: Pointer, - msg: CheckInAllocMsg, - allocation_size: Size, - }, - InvalidNullPointerUsage, - ReadPointerAsBytes, - ReadBytesAsPointer, - ReadForeignStatic, - InvalidPointerMath, - ReadUndefBytes(Size), - DeadLocal, - InvalidBoolOp(mir::BinOp), - UnimplementedTraitSelection, - CalledClosureAsFunction, - NoMirFor(String), - DerefFunctionPointer, - ExecuteMemory, - InvalidChar(u128), - OutOfTls, - TlsOutOfBounds, - AlignmentCheckFailed { - required: Align, - has: Align, - }, - ValidationFailure(String), - VtableForArgumentlessMethod, - ModifiedConstantMemory, + /// Accessing an unsupported foreign static. + ReadForeignStatic(DefId), + + /// Could not find MIR for a function. + NoMirFor(DefId), + + /// Modified a static during const-eval. + /// FIXME: move this to `ConstEvalErrKind` through a machine hook. ModifiedStatic, - TypeNotPrimitive(Ty<'tcx>), - ReallocatedWrongMemoryKind(String, String), - DeallocatedWrongMemoryKind(String, String), - ReallocateNonBasePtr, - DeallocateNonBasePtr, - IncorrectAllocationInformation(Size, Size, Align, Align), - HeapAllocZeroBytes, - HeapAllocNonPowerOfTwoAlignment(u64), - ReadFromReturnPointer, - PathNotFound(Vec), - TransmuteSizeDiff(Ty<'tcx>, Ty<'tcx>), + + /// Encountered a pointer where we needed raw bytes. + ReadPointerAsBytes, + + /// Encountered raw bytes where we needed a pointer. + ReadBytesAsPointer, } -impl fmt::Debug for UnsupportedOpInfo<'tcx> { +impl fmt::Debug for UnsupportedOpInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use UnsupportedOpInfo::*; match self { - PointerOutOfBounds { ptr, msg, allocation_size } => write!( - f, - "{} failed: pointer must be in-bounds at offset {}, \ - but is outside bounds of allocation {} which has size {}", - msg, - ptr.offset.bytes(), - ptr.alloc_id, - allocation_size.bytes() - ), - ValidationFailure(ref err) => write!(f, "type validation failed: {}", err), - NoMirFor(ref func) => write!(f, "no MIR for `{}`", func), - FunctionAbiMismatch(caller_abi, callee_abi) => write!( - f, - "tried to call a function with ABI {:?} using caller ABI {:?}", - callee_abi, caller_abi - ), - FunctionArgMismatch(caller_ty, callee_ty) => write!( - f, - "tried to call a function with argument of type {:?} \ - passing data of type {:?}", - callee_ty, caller_ty - ), - TransmuteSizeDiff(from_ty, to_ty) => write!( - f, - "tried to transmute from {:?} to {:?}, but their sizes differed", - from_ty, to_ty - ), - FunctionRetMismatch(caller_ty, callee_ty) => write!( - f, - "tried to call a function with return type {:?} \ - passing return place of type {:?}", - callee_ty, caller_ty - ), - FunctionArgCountMismatch => { - write!(f, "tried to call a function with incorrect number of arguments") + Unsupported(ref msg) => write!(f, "{}", msg), + ConstPropUnsupported(ref msg) => { + write!(f, "Constant propagation encountered an unsupported situation: {}", msg) } - ReallocatedWrongMemoryKind(ref old, ref new) => { - write!(f, "tried to reallocate memory from `{}` to `{}`", old, new) - } - DeallocatedWrongMemoryKind(ref old, ref new) => { - write!(f, "tried to deallocate `{}` memory but gave `{}` as the kind", old, new) - } - InvalidChar(c) => { - write!(f, "tried to interpret an invalid 32-bit value as a char: {}", c) - } - AlignmentCheckFailed { required, has } => write!( - f, - "tried to access memory with alignment {}, but alignment {} is required", - has.bytes(), - required.bytes() - ), - TypeNotPrimitive(ty) => write!(f, "expected primitive type, got {}", ty), - PathNotFound(ref path) => write!(f, "cannot find path {:?}", path), - IncorrectAllocationInformation(size, size2, align, align2) => write!( - f, - "incorrect alloc info: expected size {} and align {}, \ - got size {} and align {}", - size.bytes(), - align.bytes(), - size2.bytes(), - align2.bytes() - ), - InvalidMemoryAccess => write!(f, "tried to access memory through an invalid pointer"), - DanglingPointerDeref => write!(f, "dangling pointer was dereferenced"), - DoubleFree => write!(f, "tried to deallocate dangling pointer"), - InvalidFunctionPointer => { - write!(f, "tried to use a function pointer after offsetting it") - } - InvalidBool => write!(f, "invalid boolean value read"), - InvalidNullPointerUsage => write!(f, "invalid use of NULL pointer"), - ReadPointerAsBytes => write!( - f, - "a raw memory access tried to access part of a pointer value as raw \ - bytes" - ), - ReadBytesAsPointer => { - write!(f, "a memory access tried to interpret some bytes as a pointer") - } - ReadForeignStatic => write!(f, "tried to read from foreign (extern) static"), - InvalidPointerMath => write!( - f, - "attempted to do invalid arithmetic on pointers that would leak base \ - addresses, e.g., comparing pointers into different allocations" - ), - DeadLocal => write!(f, "tried to access a dead local variable"), - DerefFunctionPointer => write!(f, "tried to dereference a function pointer"), - ExecuteMemory => write!(f, "tried to treat a memory pointer as a function pointer"), - OutOfTls => write!(f, "reached the maximum number of representable TLS keys"), - TlsOutOfBounds => write!(f, "accessed an invalid (unallocated) TLS key"), - CalledClosureAsFunction => { - write!(f, "tried to call a closure through a function pointer") - } - VtableForArgumentlessMethod => { - write!(f, "tried to call a vtable function without arguments") - } - ModifiedConstantMemory => write!(f, "tried to modify constant memory"), + ReadForeignStatic(did) => write!(f, "tried to read from foreign (extern) static {:?}", did), + NoMirFor(did) => write!(f, "could not load MIR for {:?}", did), ModifiedStatic => write!( f, "tried to modify a static's initial value from another static's \ initializer" ), - ReallocateNonBasePtr => write!( + + ReadPointerAsBytes => write!( f, - "tried to reallocate with a pointer not to the beginning of an \ - existing object" + "unable to turn this pointer into raw bytes", ), - DeallocateNonBasePtr => write!( - f, - "tried to deallocate with a pointer not to the beginning of an \ - existing object" - ), - HeapAllocZeroBytes => write!(f, "tried to re-, de- or allocate zero bytes on the heap"), - ReadFromReturnPointer => write!(f, "tried to read from the return pointer"), - UnimplementedTraitSelection => { - write!(f, "there were unresolved type arguments during trait selection") - } - InvalidBoolOp(_) => write!(f, "invalid boolean operation"), - UnterminatedCString(_) => write!( - f, - "attempted to get length of a null-terminated string, but no null \ - found before end of allocation" - ), - ReadUndefBytes(_) => write!(f, "attempted to read undefined bytes"), - HeapAllocNonPowerOfTwoAlignment(_) => write!( - f, - "tried to re-, de-, or allocate heap memory with alignment that is \ - not a power of two" - ), - Unsupported(ref msg) => write!(f, "{}", msg), - ConstPropUnsupported(ref msg) => { - write!(f, "Constant propagation encountered an unsupported situation: {}", msg) + ReadBytesAsPointer => { + write!(f, "unable to turn these bytes into a pointer") } } } @@ -590,7 +521,7 @@ pub enum InterpError<'tcx> { UndefinedBehavior(UndefinedBehaviorInfo), /// The program did something the interpreter does not support (some of these *might* be UB /// but the interpreter is not sure). - Unsupported(UnsupportedOpInfo<'tcx>), + Unsupported(UnsupportedOpInfo), /// The program was invalid (ill-typed, bad MIR, not sufficiently monomorphized, ...). InvalidProgram(InvalidProgramInfo<'tcx>), /// The program exhausted the interpreter's resources (stack/heap too big, @@ -606,7 +537,7 @@ pub type InterpResult<'tcx, T = ()> = Result>; impl fmt::Display for InterpError<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Forward `Display` to `Debug`. - write!(f, "{:?}", self) + fmt::Debug::fmt(self, f) } } @@ -631,7 +562,7 @@ impl InterpError<'_> { match self { InterpError::MachineStop(_) | InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_)) - | InterpError::Unsupported(UnsupportedOpInfo::ValidationFailure(_)) + | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure(_)) | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_)) | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::UbExperimental(_)) => true, _ => false, diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 64c07b431db3..0b5bb7f3c03f 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -161,7 +161,13 @@ pub struct AllocId(pub u64); impl fmt::Debug for AllocId { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "alloc{}", self.0) + fmt::Display::fmt(self, fmt) + } +} + +impl fmt::Display for AllocId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "alloc{}", self.0) } } @@ -351,12 +357,6 @@ impl<'s> AllocDecodingSession<'s> { } } -impl fmt::Display for AllocId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - /// An allocation in the global (tcx-managed) memory can be either a function pointer, /// a static, or a "real" allocation with some data in it. #[derive(Debug, Clone, Eq, PartialEq, Hash, RustcDecodable, RustcEncodable, HashStable)] diff --git a/src/librustc/mir/interpret/pointer.rs b/src/librustc/mir/interpret/pointer.rs index cc3c50b7899f..2cbe25f96742 100644 --- a/src/librustc/mir/interpret/pointer.rs +++ b/src/librustc/mir/interpret/pointer.rs @@ -213,20 +213,4 @@ impl<'tcx, Tag> Pointer { pub fn erase_tag(self) -> Pointer { Pointer { alloc_id: self.alloc_id, offset: self.offset, tag: () } } - - /// Test if the pointer is "inbounds" of an allocation of the given size. - /// A pointer is "inbounds" even if its offset is equal to the size; this is - /// a "one-past-the-end" pointer. - #[inline(always)] - pub fn check_inbounds_alloc( - self, - allocation_size: Size, - msg: CheckInAllocMsg, - ) -> InterpResult<'tcx, ()> { - if self.offset > allocation_size { - throw_unsup!(PointerOutOfBounds { ptr: self.erase_tag(), msg, allocation_size }) - } else { - Ok(()) - } - } } diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 9dc0b24cd2f3..854f3e0146d2 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -429,10 +429,11 @@ impl<'tcx, Tag> Scalar { } pub fn to_bool(self) -> InterpResult<'tcx, bool> { - match self { - Scalar::Raw { data: 0, size: 1 } => Ok(false), - Scalar::Raw { data: 1, size: 1 } => Ok(true), - _ => throw_unsup!(InvalidBool), + let val = self.to_u8()?; + match val { + 0 => Ok(false), + 1 => Ok(true), + _ => throw_ub!(InvalidBool(val)), } } @@ -440,7 +441,7 @@ impl<'tcx, Tag> Scalar { let val = self.to_u32()?; match ::std::char::from_u32(val) { Some(c) => Ok(c), - None => throw_unsup!(InvalidChar(val as u128)), + None => throw_ub!(InvalidChar(val)), } } @@ -583,7 +584,7 @@ impl<'tcx, Tag> ScalarMaybeUndef { pub fn not_undef(self) -> InterpResult<'static, Scalar> { match self { ScalarMaybeUndef::Scalar(scalar) => Ok(scalar), - ScalarMaybeUndef::Undef => throw_unsup!(ReadUndefBytes(Size::ZERO)), + ScalarMaybeUndef::Undef => throw_ub!(InvalidUndefBytes(None)), } } diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs index bb661d3d2a30..28889486c383 100644 --- a/src/librustc_mir/const_eval/machine.rs +++ b/src/librustc_mir/const_eval/machine.rs @@ -240,7 +240,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, Ok(Some(match ecx.load_mir(instance.def, None) { Ok(body) => *body, Err(err) => { - if let err_unsup!(NoMirFor(ref path)) = err.kind { + if let err_unsup!(NoMirFor(did)) = err.kind { + let path = ecx.tcx.def_path_str(did); return Err(ConstEvalErrKind::NeedsRfc(format!( "calling extern function `{}`", path diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 9b28b7a20c04..b7888d85f38c 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -138,7 +138,7 @@ pub enum LocalValue { impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { pub fn access(&self) -> InterpResult<'tcx, Operand> { match self.value { - LocalValue::Dead => throw_unsup!(DeadLocal), + LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Uninitialized => { bug!("The type checker should prevent reading from a never-written local") } @@ -152,7 +152,7 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> { &mut self, ) -> InterpResult<'tcx, Result<&mut LocalValue, MemPlace>> { match self.value { - LocalValue::Dead => throw_unsup!(DeadLocal), + LocalValue::Dead => throw_ub!(DeadLocal), LocalValue::Live(Operand::Indirect(mplace)) => Ok(Err(mplace)), ref mut local @ LocalValue::Live(Operand::Immediate(_)) | ref mut local @ LocalValue::Uninitialized => Ok(Ok(local)), @@ -326,7 +326,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if self.tcx.is_mir_available(did) { Ok(self.tcx.optimized_mir(did).unwrap_read_only()) } else { - throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id))) + throw_unsup!(NoMirFor(def_id)) } } _ => Ok(self.tcx.instance_mir(instance)), diff --git a/src/librustc_mir/interpret/intern.rs b/src/librustc_mir/interpret/intern.rs index 9b13db2289e7..90b8a4932991 100644 --- a/src/librustc_mir/interpret/intern.rs +++ b/src/librustc_mir/interpret/intern.rs @@ -327,7 +327,7 @@ pub fn intern_const_alloc_recursive>( if let Err(error) = interned { // This can happen when e.g. the tag of an enum is not a valid discriminant. We do have // to read enum discriminants in order to find references in enum variant fields. - if let err_unsup!(ValidationFailure(_)) = error.kind { + if let err_ub!(ValidationFailure(_)) = error.kind { let err = crate::const_eval::error_to_const_error(&ecx, error); match err.struct_error( ecx.tcx, @@ -390,7 +390,7 @@ pub fn intern_const_alloc_recursive>( } } else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) { // dangling pointer - throw_unsup!(ValidationFailure("encountered dangling pointer in final constant".into())) + throw_ub_format!("encountered dangling pointer in final constant") } else if ecx.tcx.alloc_map.lock().get(alloc_id).is_none() { // We have hit an `AllocId` that is neither in local or global memory and isn't marked // as dangling by local memory. diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 1e5ed76c467b..75d936600b6b 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -134,7 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let bits = self.force_bits(val, layout_of.size)?; let kind = match layout_of.abi { ty::layout::Abi::Scalar(ref scalar) => scalar.value, - _ => throw_unsup!(TypeNotPrimitive(ty)), + _ => bug!("{} called on invalid type {:?}", intrinsic_name, ty), }; let (nonzero, intrinsic_name) = match intrinsic_name { sym::cttz_nonzero => (true, sym::cttz), diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 087517ff4e31..d86ea026ad0d 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -281,7 +281,7 @@ pub trait Machine<'mir, 'tcx>: Sized { int: u64, ) -> InterpResult<'tcx, Pointer> { Err((if int == 0 { - err_unsup!(InvalidNullPointerUsage) + err_ub!(InvalidNullPointerUsage) } else { err_unsup!(ReadBytesAsPointer) }) diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 82a467c7ba92..24147bdd44ff 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -215,7 +215,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { kind: MemoryKind, ) -> InterpResult<'tcx, Pointer> { if ptr.offset.bytes() != 0 { - throw_unsup!(ReallocateNonBasePtr) + throw_ub_format!("reallocating {:?} which does not point to the beginning of an object", ptr); } // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". @@ -251,7 +251,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { trace!("deallocating: {}", ptr.alloc_id); if ptr.offset.bytes() != 0 { - throw_unsup!(DeallocateNonBasePtr) + throw_ub_format!("deallocating {:?} which does not point to the beginning of an object", ptr); } let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) { @@ -259,29 +259,24 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { None => { // Deallocating static memory -- always an error return Err(match self.tcx.alloc_map.lock().get(ptr.alloc_id) { - Some(GlobalAlloc::Function(..)) => err_unsup!(DeallocatedWrongMemoryKind( - "function".to_string(), - format!("{:?}", kind), - )), - Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => err_unsup!( - DeallocatedWrongMemoryKind("static".to_string(), format!("{:?}", kind)) - ), - None => err_unsup!(DoubleFree), + Some(GlobalAlloc::Function(..)) => err_ub_format!("deallocating a function"), + Some(GlobalAlloc::Static(..)) | Some(GlobalAlloc::Memory(..)) => + err_ub_format!("deallocating static memory"), + None => err_ub!(PointerUseAfterFree(ptr.alloc_id)), } .into()); } }; if alloc_kind != kind { - throw_unsup!(DeallocatedWrongMemoryKind( - format!("{:?}", alloc_kind), - format!("{:?}", kind), - )) + throw_ub_format!("deallocating `{:?}` memory using `{:?}` deallocation operation", alloc_kind, kind); } if let Some((size, align)) = old_size_and_align { if size != alloc.size || align != alloc.align { - let bytes = alloc.size; - throw_unsup!(IncorrectAllocationInformation(size, bytes, align, alloc.align)) + throw_ub_format!( + "incorrect layout on deallocation: allocation has size {} and alignment {}, but gave size {} and alignment {}", + alloc.size.bytes(), alloc.align.bytes(), size.bytes(), align.bytes(), + ) } } @@ -338,7 +333,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { } else { // The biggest power of two through which `offset` is divisible. let offset_pow2 = 1 << offset.trailing_zeros(); - throw_unsup!(AlignmentCheckFailed { + throw_ub!(AlignmentCheckFailed { has: Align::from_bytes(offset_pow2).unwrap(), required: align, }) @@ -360,7 +355,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { assert!(size.bytes() == 0); // Must be non-NULL. if bits == 0 { - throw_unsup!(InvalidNullPointerUsage) + throw_ub!(InvalidNullPointerUsage) } // Must be aligned. if let Some(align) = align { @@ -375,7 +370,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // It is sufficient to check this for the end pointer. The addition // checks for overflow. let end_ptr = ptr.offset(size, self)?; - end_ptr.check_inbounds_alloc(allocation_size, msg)?; + if end_ptr.offset > allocation_size { // equal is okay! + throw_ub!(PointerOutOfBounds { ptr: end_ptr.erase_tag(), msg, allocation_size }) + } // Test align. Check this last; if both bounds and alignment are violated // we want the error to be about the bounds. if let Some(align) = align { @@ -385,7 +382,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // got picked we might be aligned even if this check fails. // We instead have to fall back to converting to an integer and checking // the "real" alignment. - throw_unsup!(AlignmentCheckFailed { has: alloc_align, required: align }); + throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align }); } check_offset_align(ptr.offset.bytes(), align)?; } @@ -402,7 +399,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let (size, _align) = self .get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead) .expect("alloc info with MaybeDead cannot fail"); - ptr.check_inbounds_alloc(size, CheckInAllocMsg::NullPointerTest).is_err() + // An inbounds pointer is never null! And "inbounds" includes one-past-the-end. + let inbounds = ptr.offset <= size; + !inbounds } } @@ -432,13 +431,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { let alloc = tcx.alloc_map.lock().get(id); let alloc = match alloc { Some(GlobalAlloc::Memory(mem)) => Cow::Borrowed(mem), - Some(GlobalAlloc::Function(..)) => throw_unsup!(DerefFunctionPointer), - None => throw_unsup!(DanglingPointerDeref), + Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)), + None => throw_ub!(PointerUseAfterFree(id)), Some(GlobalAlloc::Static(def_id)) => { // We got a "lazy" static that has not been computed yet. if tcx.is_foreign_item(def_id) { trace!("get_static_alloc: foreign item {:?}", def_id); - throw_unsup!(ReadForeignStatic) + throw_unsup!(ReadForeignStatic(def_id)) } trace!("get_static_alloc: Need to compute {:?}", def_id); let instance = Instance::mono(tcx.tcx, def_id); @@ -524,7 +523,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { // to give us a cheap reference. let alloc = Self::get_static_alloc(memory_extra, tcx, id)?; if alloc.mutability == Mutability::Not { - throw_unsup!(ModifiedConstantMemory) + throw_ub!(WriteToReadOnly(id)) } match M::STATIC_KIND { Some(kind) => Ok((MemoryKind::Machine(kind), alloc.into_owned())), @@ -538,7 +537,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Ok(a) => { let a = &mut a.1; if a.mutability == Mutability::Not { - throw_unsup!(ModifiedConstantMemory) + throw_ub!(WriteToReadOnly(id)) } Ok(a) } @@ -568,7 +567,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if self.get_fn_alloc(id).is_some() { return if let AllocCheck::Dereferenceable = liveness { // The caller requested no function pointers. - throw_unsup!(DerefFunctionPointer) + throw_ub!(DerefFunctionPointer(id)) } else { Ok((Size::ZERO, Align::from_bytes(1).unwrap())) }; @@ -596,12 +595,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { if let AllocCheck::MaybeDead = liveness { // Deallocated pointers are allowed, we should be able to find // them in the map. - Ok(*self.dead_alloc_map.get(&id).expect( - "deallocated pointers should all be recorded in \ - `dead_alloc_map`", - )) + Ok(*self + .dead_alloc_map + .get(&id) + .expect("deallocated pointers should all be recorded in `dead_alloc_map`")) } else { - throw_unsup!(DanglingPointerDeref) + throw_ub!(PointerUseAfterFree(id)) } } } @@ -626,10 +625,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let ptr = self.force_ptr(ptr)?; // We definitely need a pointer value. if ptr.offset.bytes() != 0 { - throw_unsup!(InvalidFunctionPointer) + throw_ub!(InvalidFunctionPointer(ptr.erase_tag())) } let id = M::canonical_alloc_id(self, ptr.alloc_id); - self.get_fn_alloc(id).ok_or_else(|| err_unsup!(ExecuteMemory).into()) + self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into()) } pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> { diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 22b1a7b7137d..227ba540e6b9 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -344,7 +344,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let len = mplace.len(self)?; let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len as u64))?; let str = ::std::str::from_utf8(bytes) - .map_err(|err| err_unsup!(ValidationFailure(err.to_string())))?; + .map_err(|err| err_ub_format!("this string is not valid UTF-8: {}", err))?; Ok(str) } @@ -458,7 +458,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { let base_op = match place.local { - mir::RETURN_PLACE => throw_unsup!(ReadFromReturnPointer), + mir::RETURN_PLACE => throw_ub!(ReadFromReturnPlace), local => { // Do not use the layout passed in as argument if the base we are looking at // here is not the entire place. diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index a4815b9696eb..107cfee5aceb 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -926,7 +926,7 @@ where // most likey we *are* running `typeck` right now. Investigate whether we can bail out // on `typeck_tables().has_errors` at all const eval entry points. debug!("Size mismatch when transmuting!\nsrc: {:#?}\ndest: {:#?}", src, dest); - throw_unsup!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); + throw_inval!(TransmuteSizeDiff(src.layout.ty, dest.layout.ty)); } // Unsized copies rely on interpreting `src.meta` with `dest.layout`, we want // to avoid that here. diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index b5c34daf8a31..51a557851fce 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -170,13 +170,19 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { trace!("Skipping callee ZST"); return Ok(()); } - let caller_arg = caller_arg.next().ok_or_else(|| err_unsup!(FunctionArgCountMismatch))?; + let caller_arg = caller_arg.next().ok_or_else(|| { + err_ub_format!("calling a function passing fewer arguments than it requires") + })?; if rust_abi { assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out"); } // Now, check if !Self::check_argument_compat(rust_abi, caller_arg.layout, callee_arg.layout) { - throw_unsup!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty)) + throw_ub_format!( + "calling a function with argument of type {:?} passing data of type {:?}", + callee_arg.layout.ty, + caller_arg.layout.ty + ) } // We allow some transmutes here self.copy_op_transmute(caller_arg, callee_arg) @@ -221,7 +227,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { abi => abi, }; if normalize_abi(caller_abi) != normalize_abi(callee_abi) { - throw_unsup!(FunctionAbiMismatch(caller_abi, callee_abi)) + throw_ub_format!( + "calling a function with ABI {:?} using caller ABI {:?}", + callee_abi, + caller_abi + ) } } @@ -254,107 +264,110 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // We want to pop this frame again in case there was an error, to put // the blame in the right location. Until the 2018 edition is used in // the compiler, we have to do this with an immediately invoked function. - let res = - (|| { - trace!( - "caller ABI: {:?}, args: {:#?}", - caller_abi, - args.iter() - .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) - .collect::>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - body.spread_arg, - body.args_iter() - .map(|local| ( - local, - self.layout_of_local(self.frame(), local, None).unwrap().ty - )) - .collect::>() - ); + let res = (|| { + trace!( + "caller ABI: {:?}, args: {:#?}", + caller_abi, + args.iter() + .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) + .collect::>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + body.spread_arg, + body.args_iter() + .map(|local| ( + local, + self.layout_of_local(self.frame(), local, None).unwrap().ty + )) + .collect::>() + ); - // Figure out how to pass which arguments. - // The Rust ABI is special: ZST get skipped. - let rust_abi = match caller_abi { - Abi::Rust | Abi::RustCall => true, - _ => false, + // Figure out how to pass which arguments. + // The Rust ABI is special: ZST get skipped. + let rust_abi = match caller_abi { + Abi::Rust | Abi::RustCall => true, + _ => false, + }; + // We have two iterators: Where the arguments come from, + // and where they go to. + + // For where they come from: If the ABI is RustCall, we untuple the + // last incoming argument. These two iterators do not have the same type, + // so to keep the code paths uniform we accept an allocation + // (for RustCall ABI only). + let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = + if caller_abi == Abi::RustCall && !args.is_empty() { + // Untuple + let (&untuple_arg, args) = args.split_last().unwrap(); + trace!("eval_fn_call: Will pass last argument by untupling"); + Cow::from( + args.iter() + .map(|&a| Ok(a)) + .chain( + (0..untuple_arg.layout.fields.count()) + .map(|i| self.operand_field(untuple_arg, i as u64)), + ) + .collect::>>>( + )?, + ) + } else { + // Plain arg passing + Cow::from(args) }; - // We have two iterators: Where the arguments come from, - // and where they go to. + // Skip ZSTs + let mut caller_iter = + caller_args.iter().filter(|op| !rust_abi || !op.layout.is_zst()).copied(); - // For where they come from: If the ABI is RustCall, we untuple the - // last incoming argument. These two iterators do not have the same type, - // so to keep the code paths uniform we accept an allocation - // (for RustCall ABI only). - let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> = - if caller_abi == Abi::RustCall && !args.is_empty() { - // Untuple - let (&untuple_arg, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - Cow::from(args.iter().map(|&a| Ok(a)) - .chain((0..untuple_arg.layout.fields.count()) - .map(|i| self.operand_field(untuple_arg, i as u64)) - ) - .collect::>>>()?) - } else { - // Plain arg passing - Cow::from(args) - }; - // Skip ZSTs - let mut caller_iter = caller_args - .iter() - .filter(|op| !rust_abi || !op.layout.is_zst()) - .copied(); - - // Now we have to spread them out across the callee's locals, - // taking into account the `spread_arg`. If we could write - // this is a single iterator (that handles `spread_arg`), then - // `pass_argument` would be the loop body. It takes care to - // not advance `caller_iter` for ZSTs - for local in body.args_iter() { - let dest = self.eval_place(&mir::Place::from(local))?; - if Some(local) == body.spread_arg { - // Must be a tuple - for i in 0..dest.layout.fields.count() { - let dest = self.place_field(dest, i as u64)?; - self.pass_argument(rust_abi, &mut caller_iter, dest)?; - } - } else { - // Normal argument + // Now we have to spread them out across the callee's locals, + // taking into account the `spread_arg`. If we could write + // this is a single iterator (that handles `spread_arg`), then + // `pass_argument` would be the loop body. It takes care to + // not advance `caller_iter` for ZSTs. + let mut locals_iter = body.args_iter(); + while let Some(local) = locals_iter.next() { + let dest = self.eval_place(&mir::Place::from(local))?; + if Some(local) == body.spread_arg { + // Must be a tuple + for i in 0..dest.layout.fields.count() { + let dest = self.place_field(dest, i as u64)?; self.pass_argument(rust_abi, &mut caller_iter, dest)?; } - } - // Now we should have no more caller args - if caller_iter.next().is_some() { - trace!("Caller has passed too many args"); - throw_unsup!(FunctionArgCountMismatch) - } - // Don't forget to check the return type! - if let Some((caller_ret, _)) = ret { - let callee_ret = self.eval_place(&mir::Place::return_place())?; - if !Self::check_argument_compat( - rust_abi, - caller_ret.layout, - callee_ret.layout, - ) { - throw_unsup!(FunctionRetMismatch( - caller_ret.layout.ty, - callee_ret.layout.ty - )) - } } else { - let local = mir::RETURN_PLACE; - let callee_layout = self.layout_of_local(self.frame(), local, None)?; - if !callee_layout.abi.is_uninhabited() { - throw_unsup!(FunctionRetMismatch( - self.tcx.types.never, - callee_layout.ty - )) - } + // Normal argument + self.pass_argument(rust_abi, &mut caller_iter, dest)?; } - Ok(()) - })(); + } + // Now we should have no more caller args + if caller_iter.next().is_some() { + throw_ub_format!( + "calling a function passing more arguments than it expected" + ) + } + // Don't forget to check the return type! + if let Some((caller_ret, _)) = ret { + let callee_ret = self.eval_place(&mir::Place::return_place())?; + if !Self::check_argument_compat( + rust_abi, + caller_ret.layout, + callee_ret.layout, + ) { + throw_ub_format!( + "calling a function with return type {:?} passing \ + return place of type {:?}", + callee_ret.layout.ty, + caller_ret.layout.ty + ) + } + } else { + let local = mir::RETURN_PLACE; + let callee_layout = self.layout_of_local(self.frame(), local, None)?; + if !callee_layout.abi.is_uninhabited() { + throw_ub_format!("calling a returning function without a return place") + } + } + Ok(()) + })(); match res { Err(err) => { self.stack.pop(); diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 05bb010959b3..b3a09cb81ba0 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -29,7 +29,7 @@ macro_rules! throw_validation_failure { write_path(&mut msg, where_); } write!(&mut msg, ", but expected {}", $details).unwrap(); - throw_unsup!(ValidationFailure(msg)) + throw_ub!(ValidationFailure(msg)) }}; ($what:expr, $where:expr) => {{ let mut msg = format!("encountered {}", $what); @@ -38,7 +38,7 @@ macro_rules! throw_validation_failure { msg.push_str(" at "); write_path(&mut msg, where_); } - throw_unsup!(ValidationFailure(msg)) + throw_ub!(ValidationFailure(msg)) }}; } @@ -353,10 +353,10 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M place.ptr, size, align ); match err.kind { - err_unsup!(InvalidNullPointerUsage) => { + err_ub!(InvalidNullPointerUsage) => { throw_validation_failure!(format_args!("a NULL {}", kind), self.path) } - err_unsup!(AlignmentCheckFailed { required, has }) => { + err_ub!(AlignmentCheckFailed { required, has }) => { throw_validation_failure!( format_args!( "an unaligned {} \ @@ -372,7 +372,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, 'tcx, M format_args!("a dangling {} (created from integer)", kind), self.path ), - err_unsup!(PointerOutOfBounds { .. }) | err_unsup!(DanglingPointerDeref) => { + err_ub!(PointerOutOfBounds { .. }) | err_ub!(PointerUseAfterFree(_)) => { throw_validation_failure!( format_args!("a dangling {} (not entirely in bounds)", kind), self.path @@ -765,11 +765,11 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> Err(err) => { // For some errors we might be able to provide extra information match err.kind { - err_unsup!(ReadUndefBytes(offset)) => { + err_ub!(InvalidUndefBytes(Some(ptr))) => { // Some byte was undefined, determine which // element that byte belongs to so we can // provide an index. - let i = (offset.bytes() / layout.size.bytes()) as usize; + let i = (ptr.offset.bytes() / layout.size.bytes()) as usize; self.path.push(PathElem::ArrayElem(i)); throw_validation_failure!("undefined bytes", self.path) @@ -817,7 +817,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Run it. match visitor.visit_value(op) { Ok(()) => Ok(()), - Err(err) if matches!(err.kind, err_unsup!(ValidationFailure { .. })) => Err(err), + Err(err) if matches!(err.kind, err_ub!(ValidationFailure { .. })) => Err(err), Err(err) if cfg!(debug_assertions) => { bug!("Unexpected error during validation: {}", err) }