@@ -12,11 +12,11 @@ use rustc_middle::mir::{
1212} ;
1313use rustc_middle:: ty;
1414use rustc_middle:: ty:: subst:: SubstsRef ;
15- use rustc_middle:: ty:: TyCtxt ;
15+ use rustc_middle:: ty:: { Ty , TyCtxt } ;
1616use rustc_span:: symbol:: { sym, Symbol } ;
1717use rustc_target:: abi:: { Abi , LayoutOf as _, Primitive , Size } ;
1818
19- use super :: { ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
19+ use super :: { CheckInAllocMsg , ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
2020
2121mod caller_location;
2222mod type_name;
@@ -273,7 +273,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
273273 let result = Scalar :: from_uint ( truncated_bits, layout. size ) ;
274274 self . write_scalar ( result, dest) ?;
275275 }
276+ sym:: offset => {
277+ let ptr = self . read_scalar ( args[ 0 ] ) ?. not_undef ( ) ?;
278+ let offset_count = self . read_scalar ( args[ 1 ] ) ?. to_machine_isize ( self ) ?;
279+ let pointee_ty = substs. type_at ( 0 ) ;
276280
281+ let offset_ptr = self . ptr_offset_inbounds ( ptr, pointee_ty, offset_count) ?;
282+ self . write_scalar ( offset_ptr, dest) ?;
283+ }
284+ sym:: arith_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 pointee_size = i64:: try_from ( self . layout_of ( pointee_ty) ?. size . bytes ( ) ) . unwrap ( ) ;
290+ let offset_bytes = offset_count. wrapping_mul ( pointee_size) ;
291+ let offset_ptr = ptr. ptr_wrapping_signed_offset ( offset_bytes, self ) ;
292+ self . write_scalar ( offset_ptr, dest) ?;
293+ }
277294 sym:: ptr_offset_from => {
278295 let a = self . read_immediate ( args[ 0 ] ) ?. to_scalar ( ) ?;
279296 let b = self . read_immediate ( args[ 1 ] ) ?. to_scalar ( ) ?;
@@ -403,4 +420,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
403420 // `Rem` says this is all right, so we can let `Div` do its job.
404421 self . binop_ignore_overflow ( BinOp :: Div , a, b, dest)
405422 }
423+
424+ /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
425+ /// allocation. For integer pointers, we consider each of them their own tiny allocation of size
426+ /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value.
427+ pub fn ptr_offset_inbounds (
428+ & self ,
429+ ptr : Scalar < M :: PointerTag > ,
430+ pointee_ty : Ty < ' tcx > ,
431+ offset_count : i64 ,
432+ ) -> InterpResult < ' tcx , Scalar < M :: PointerTag > > {
433+ let pointee_size = i64:: try_from ( self . layout_of ( pointee_ty) ?. size . bytes ( ) ) . unwrap ( ) ;
434+ // The computed offset, in bytes, cannot overflow an isize.
435+ let offset_bytes = offset_count
436+ . checked_mul ( pointee_size)
437+ . ok_or ( err_ub_format ! ( "inbounds pointer arithmetic: overflow computing offset" ) ) ?;
438+ // The offset being in bounds cannot rely on "wrapping around" the address space.
439+ // So, first rule out overflows in the pointer arithmetic.
440+ let offset_ptr = ptr. ptr_signed_offset ( offset_bytes, self ) ?;
441+ // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the
442+ // memory between these pointers must be accessible. Note that we do not require the
443+ // pointers to be properly aligned (unlike a read/write operation).
444+ let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr } ;
445+ let size = offset_bytes. checked_abs ( ) . unwrap ( ) ;
446+ // This call handles checking for integer/NULL pointers.
447+ self . memory . check_ptr_access_align (
448+ min_ptr,
449+ Size :: from_bytes ( size) ,
450+ None ,
451+ CheckInAllocMsg :: InboundsTest ,
452+ ) ?;
453+ Ok ( offset_ptr)
454+ }
406455}
0 commit comments