1
Fork 0

fix the way we detect overflow for inbounds arithmetic (and tweak the error message)

This commit is contained in:
Ralf Jung 2024-08-01 14:38:58 +02:00
parent 5d5c97aad7
commit db1652e07b
3 changed files with 17 additions and 11 deletions

View file

@ -7,7 +7,7 @@ use rustc_middle::{bug, mir, span_bug};
use rustc_span::symbol::sym;
use tracing::trace;
use super::{err_ub, throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
use super::{throw_ub, ImmTy, InterpCx, Machine, MemPlaceMeta};
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
fn three_way_compare<T: Ord>(&self, lhs: T, rhs: T) -> ImmTy<'tcx, M::Provenance> {
@ -298,17 +298,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// Pointer ops that are always supported.
Offset => {
let ptr = left.to_scalar().to_pointer(self)?;
let offset_count = right.to_scalar().to_target_isize(self)?;
let pointee_ty = left.layout.ty.builtin_deref(true).unwrap();
let pointee_layout = self.layout_of(pointee_ty)?;
assert!(pointee_layout.abi.is_sized());
// We cannot overflow i64 as a type's size must be <= isize::MAX.
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
// The computed offset, in bytes, must not overflow an isize.
// `checked_mul` enforces a too small bound, but no actual allocation can be big enough for
// the difference to be noticeable.
let offset_bytes =
offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?;
let pointee_size = i64::try_from(pointee_layout.size.bytes()).unwrap();
let pointee_size = ImmTy::from_int(pointee_size, right.layout);
// Multiply element size and element count.
let (val, overflowed) = self
.binary_op(mir::BinOp::MulWithOverflow, right, &pointee_size)?
.to_scalar_pair();
// This must not overflow.
if overflowed.to_bool()? {
throw_ub!(PointerArithOverflow)
}
let offset_bytes = val.to_target_isize(self)?;
let offset_ptr = self.ptr_offset_inbounds(ptr, offset_bytes)?;
Ok(ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout))
}