|
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