@@ -10,11 +10,11 @@ use rustc_middle::mir::{
10
10
} ;
11
11
use rustc_middle:: ty;
12
12
use rustc_middle:: ty:: subst:: SubstsRef ;
13
- use rustc_middle:: ty:: TyCtxt ;
13
+ use rustc_middle:: ty:: { Ty , TyCtxt } ;
14
14
use rustc_span:: symbol:: { sym, Symbol } ;
15
15
use rustc_target:: abi:: { Abi , LayoutOf as _, Primitive , Size } ;
16
16
17
- use super :: { ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
17
+ use super :: { CheckInAllocMsg , ImmTy , InterpCx , Machine , OpTy , PlaceTy } ;
18
18
19
19
mod caller_location;
20
20
mod type_name;
@@ -279,7 +279,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
279
279
let result = Scalar :: from_uint ( truncated_bits, layout. size ) ;
280
280
self . write_scalar ( result, dest) ?;
281
281
}
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 ) ;
282
286
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
+ }
283
300
sym:: ptr_offset_from => {
284
301
let a = self . read_immediate ( args[ 0 ] ) ?. to_scalar ( ) ?;
285
302
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> {
409
426
// `Rem` says this is all right, so we can let `Div` do its job.
410
427
self . binop_ignore_overflow ( BinOp :: Div , a, b, dest)
411
428
}
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
+ }
412
461
}
0 commit comments