@@ -10,11 +10,11 @@ use rustc_middle::mir::{
1010} ;
1111use rustc_middle:: ty;
1212use rustc_middle:: ty:: subst:: SubstsRef ;
13- use rustc_middle:: ty:: TyCtxt ;
13+ use rustc_middle:: ty:: { Ty , TyCtxt } ;
1414use rustc_span:: symbol:: { sym, Symbol } ;
1515use rustc_target:: abi:: { Abi , LayoutOf as _, Primitive , Size } ;
1616
17- use super :: { ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
17+ use super :: { CheckInAllocMsg , ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
1818
1919mod caller_location;
2020mod type_name;
@@ -279,7 +279,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279279 let result = Scalar :: from_uint ( truncated_bits, layout. size ) ;
280280 self . write_scalar ( result, dest) ?;
281281 }
282+ sym:: offset => {
283+ let ptr = self . read_scalar ( args[ 0 ] ) ?. not_undef ( ) ?;
284+ let offset_count = self . read_scalar ( args[ 1 ] ) ?. to_machine_isize ( self ) ?;
285+ let pointee_ty = substs. type_at ( 0 ) ;
282286
287+ let offset_ptr = self . ptr_offset_inbounds ( ptr, pointee_ty, offset_count) ?;
288+ self . write_scalar ( offset_ptr, dest) ?;
289+ }
290+ sym:: arith_offset => {
291+ let ptr = self . read_scalar ( args[ 0 ] ) ?. not_undef ( ) ?;
292+ let offset_count = self . read_scalar ( args[ 1 ] ) ?. to_machine_isize ( self ) ?;
293+ let pointee_ty = substs. type_at ( 0 ) ;
294+
295+ let pointee_size = i64:: try_from ( self . layout_of ( pointee_ty) ?. size . bytes ( ) ) . unwrap ( ) ;
296+ let offset_bytes = offset_count. wrapping_mul ( pointee_size) ;
297+ let offset_ptr = ptr. ptr_wrapping_signed_offset ( offset_bytes, self ) ;
298+ self . write_scalar ( offset_ptr, dest) ?;
299+ }
283300 sym:: ptr_offset_from => {
284301 let a = self . read_immediate ( args[ 0 ] ) ?. to_scalar ( ) ?;
285302 let b = self . read_immediate ( args[ 1 ] ) ?. to_scalar ( ) ?;
@@ -409,4 +426,36 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
409426 // `Rem` says this is all right, so we can let `Div` do its job.
410427 self . binop_ignore_overflow ( BinOp :: Div , a, b, dest)
411428 }
429+
430+ /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
431+ /// allocation. For integer pointers, we consider each of them their own tiny allocation of size
432+ /// 0, so offset-by-0 (and only 0) is okay -- except that NULL cannot be offset by _any_ value.
433+ pub fn ptr_offset_inbounds (
434+ & self ,
435+ ptr : Scalar < M :: PointerTag > ,
436+ pointee_ty : Ty < ' tcx > ,
437+ offset_count : i64 ,
438+ ) -> InterpResult < ' tcx , Scalar < M :: PointerTag > > {
439+ let pointee_size = i64:: try_from ( self . layout_of ( pointee_ty) ?. size . bytes ( ) ) . unwrap ( ) ;
440+ // The computed offset, in bytes, cannot overflow an isize.
441+ let offset_bytes = offset_count
442+ . checked_mul ( pointee_size)
443+ . ok_or ( err_ub_format ! ( "inbounds pointer arithmetic: overflow computing offset" ) ) ?;
444+ // The offset being in bounds cannot rely on "wrapping around" the address space.
445+ // So, first rule out overflows in the pointer arithmetic.
446+ let offset_ptr = ptr. ptr_signed_offset ( offset_bytes, self ) ?;
447+ // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the
448+ // memory between these pointers must be accessible. Note that we do not require the
449+ // pointers to be properly aligned (unlike a read/write operation).
450+ let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr } ;
451+ let size = offset_bytes. checked_abs ( ) . unwrap ( ) ;
452+ // This call handles checking for integer/NULL pointers.
453+ self . memory . check_ptr_access_align (
454+ min_ptr,
455+ Size :: from_bytes ( size) ,
456+ None ,
457+ CheckInAllocMsg :: InboundsTest ,
458+ ) ?;
459+ Ok ( offset_ptr)
460+ }
412461}
0 commit comments