@@ -12,11 +12,11 @@ use rustc_middle::mir::{
12
12
} ;
13
13
use rustc_middle:: ty;
14
14
use rustc_middle:: ty:: subst:: SubstsRef ;
15
- use rustc_middle:: ty:: TyCtxt ;
15
+ use rustc_middle:: ty:: { Ty , TyCtxt } ;
16
16
use rustc_span:: symbol:: { sym, Symbol } ;
17
17
use rustc_target:: abi:: { Abi , LayoutOf as _, Primitive , Size } ;
18
18
19
- use super :: { ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
19
+ use super :: { CheckInAllocMsg , ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
20
20
21
21
mod caller_location;
22
22
mod type_name;
@@ -273,7 +273,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
273
273
let result = Scalar :: from_uint ( truncated_bits, layout. size ) ;
274
274
self . write_scalar ( result, dest) ?;
275
275
}
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 ) ;
276
280
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
+ }
277
294
sym:: ptr_offset_from => {
278
295
let a = self . read_immediate ( args[ 0 ] ) ?. to_scalar ( ) ?;
279
296
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> {
403
420
// `Rem` says this is all right, so we can let `Div` do its job.
404
421
self . binop_ignore_overflow ( BinOp :: Div , a, b, dest)
405
422
}
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
+ }
406
455
}
0 commit comments