|
2 | 2 | //! looking at their MIR. Intrinsics/functions supported here are shared by CTFE |
3 | 3 | //! and miri. |
4 | 4 |
|
| 5 | +use std::convert::TryFrom; |
| 6 | + |
5 | 7 | use rustc_hir::def_id::DefId; |
6 | 8 | use rustc_middle::mir::{ |
7 | 9 | self, |
8 | | - interpret::{ConstValue, GlobalId, InterpResult, Scalar}, |
| 10 | + interpret::{uabs, ConstValue, GlobalId, InterpResult, Scalar}, |
9 | 11 | BinOp, |
10 | 12 | }; |
11 | 13 | use rustc_middle::ty; |
12 | 14 | use rustc_middle::ty::subst::SubstsRef; |
13 | | -use rustc_middle::ty::TyCtxt; |
| 15 | +use rustc_middle::ty::{Ty, TyCtxt}; |
14 | 16 | use rustc_span::symbol::{sym, Symbol}; |
15 | 17 | use rustc_target::abi::{Abi, LayoutOf as _, Primitive, Size}; |
16 | 18 |
|
17 | | -use super::{ImmTy, InterpCx, Machine, OpTy, PlaceTy}; |
| 19 | +use super::{CheckInAllocMsg, ImmTy, InterpCx, Machine, OpTy, PlaceTy}; |
18 | 20 |
|
19 | 21 | mod caller_location; |
20 | 22 | mod type_name; |
@@ -279,7 +281,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
279 | 281 | let result = Scalar::from_uint(truncated_bits, layout.size); |
280 | 282 | self.write_scalar(result, dest)?; |
281 | 283 | } |
| 284 | + sym::offset => { |
| 285 | + let ptr = self.read_scalar(args[0])?.not_undef()?; |
| 286 | + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; |
| 287 | + let pointee_ty = substs.type_at(0); |
| 288 | + |
| 289 | + let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; |
| 290 | + self.write_scalar(offset_ptr, dest)?; |
| 291 | + } |
| 292 | + sym::arith_offset => { |
| 293 | + let ptr = self.read_scalar(args[0])?.not_undef()?; |
| 294 | + let offset_count = self.read_scalar(args[1])?.to_machine_isize(self)?; |
| 295 | + let pointee_ty = substs.type_at(0); |
282 | 296 |
|
| 297 | + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); |
| 298 | + let offset_bytes = offset_count.wrapping_mul(pointee_size); |
| 299 | + let offset_ptr = ptr.ptr_wrapping_signed_offset(offset_bytes, self); |
| 300 | + self.write_scalar(offset_ptr, dest)?; |
| 301 | + } |
283 | 302 | sym::ptr_offset_from => { |
284 | 303 | let a = self.read_immediate(args[0])?.to_scalar()?; |
285 | 304 | let b = self.read_immediate(args[1])?.to_scalar()?; |
@@ -409,4 +428,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
409 | 428 | // `Rem` says this is all right, so we can let `Div` do its job. |
410 | 429 | self.binop_ignore_overflow(BinOp::Div, a, b, dest) |
411 | 430 | } |
| 431 | + |
| 432 | + /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its |
| 433 | + /// allocation. For integer pointers, we consider each of them their own tiny allocation of size |
| 434 | + /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value. |
| 435 | + pub fn ptr_offset_inbounds( |
| 436 | + &self, |
| 437 | + ptr: Scalar<M::PointerTag>, |
| 438 | + pointee_ty: Ty<'tcx>, |
| 439 | + offset_count: i64, |
| 440 | + ) -> InterpResult<'tcx, Scalar<M::PointerTag>> { |
| 441 | + // We cannot overflow i64 as a type's size must be <= isize::MAX. |
| 442 | + let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap(); |
| 443 | + // The computed offset, in bytes, cannot overflow an isize. |
| 444 | + let offset_bytes = |
| 445 | + offset_count.checked_mul(pointee_size).ok_or(err_ub!(PointerArithOverflow))?; |
| 446 | + // The offset being in bounds cannot rely on "wrapping around" the address space. |
| 447 | + // So, first rule out overflows in the pointer arithmetic. |
| 448 | + let offset_ptr = ptr.ptr_signed_offset(offset_bytes, self)?; |
| 449 | + // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the |
| 450 | + // memory between these pointers must be accessible. Note that we do not require the |
| 451 | + // pointers to be properly aligned (unlike a read/write operation). |
| 452 | + let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr }; |
| 453 | + let size: u64 = uabs(offset_bytes); |
| 454 | + // This call handles checking for integer/NULL pointers. |
| 455 | + self.memory.check_ptr_access_align( |
| 456 | + min_ptr, |
| 457 | + Size::from_bytes(size), |
| 458 | + None, |
| 459 | + CheckInAllocMsg::InboundsTest, |
| 460 | + )?; |
| 461 | + Ok(offset_ptr) |
| 462 | + } |
412 | 463 | } |
0 commit comments