@@ -313,78 +313,82 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
313
313
let a = self . read_pointer ( & args[ 0 ] ) ?;
314
314
let b = self . read_pointer ( & args[ 1 ] ) ?;
315
315
316
- // Special case: if both scalars are *equal integers*
317
- // and not null, we pretend there is an allocation of size 0 right there,
318
- // and their offset is 0. (There's never a valid object at null, making it an
319
- // exception from the exception.)
320
- // This is the dual to the special exception for offset-by-0
321
- // in the inbounds pointer offset operation (see `ptr_offset_inbounds` below).
322
- match ( self . ptr_try_get_alloc_id ( a) , self . ptr_try_get_alloc_id ( b) ) {
323
- ( Err ( a) , Err ( b) ) if a == b && a != 0 => {
324
- // Both are the same non-null integer.
325
- self . write_scalar ( Scalar :: from_machine_isize ( 0 , self ) , dest) ?;
326
- }
327
- ( Err ( offset) , _) | ( _, Err ( offset) ) => {
328
- throw_ub ! ( DanglingIntPointer ( offset, CheckInAllocMsg :: OffsetFromTest ) ) ;
329
- }
330
- ( Ok ( ( a_alloc_id, a_offset, _) ) , Ok ( ( b_alloc_id, b_offset, _) ) ) => {
331
- // Both are pointers. They must be into the same allocation.
332
- if a_alloc_id != b_alloc_id {
333
- throw_ub_format ! (
334
- "{} cannot compute offset of pointers into different allocations." ,
335
- intrinsic_name,
336
- ) ;
316
+ let usize_layout = self . layout_of ( self . tcx . types . usize ) ?;
317
+ let isize_layout = self . layout_of ( self . tcx . types . isize ) ?;
318
+
319
+ // Get offsets for both that are at least relative to the same base.
320
+ let ( a_offset, b_offset) =
321
+ match ( self . ptr_try_get_alloc_id ( a) , self . ptr_try_get_alloc_id ( b) ) {
322
+ ( Err ( a) , Err ( b) ) => {
323
+ // Neither poiner points to an allocation.
324
+ // If these are inequal or null, this *will* fail the deref check below.
325
+ ( a, b)
337
326
}
338
- // And they must both be valid for zero-sized accesses ("in-bounds or one past the end").
339
- self . check_ptr_access_align (
340
- a,
341
- Size :: ZERO ,
342
- Align :: ONE ,
343
- CheckInAllocMsg :: OffsetFromTest ,
344
- ) ?;
345
- self . check_ptr_access_align (
346
- b,
347
- Size :: ZERO ,
348
- Align :: ONE ,
349
- CheckInAllocMsg :: OffsetFromTest ,
350
- ) ?;
351
-
352
- if intrinsic_name == sym:: ptr_offset_from_unsigned && a_offset < b_offset {
327
+ ( Err ( _) , _) | ( _, Err ( _) ) => {
328
+ // We managed to find a valid allocation for one pointer, but not the other.
329
+ // That means they are definitely not pointing to the same allocation.
353
330
throw_ub_format ! (
354
- "{} cannot compute a negative offset, but {} < {}" ,
355
- intrinsic_name,
356
- a_offset. bytes( ) ,
357
- b_offset. bytes( ) ,
331
+ "{} called on pointers into different allocations" ,
332
+ intrinsic_name
358
333
) ;
359
334
}
360
-
361
- // Compute offset.
362
- let usize_layout = self . layout_of ( self . tcx . types . usize ) ?;
363
- let isize_layout = self . layout_of ( self . tcx . types . isize ) ?;
364
- let ret_layout = if intrinsic_name == sym:: ptr_offset_from {
365
- isize_layout
366
- } else {
367
- usize_layout
368
- } ;
369
-
370
- // The subtraction is always done in `isize` to enforce
371
- // the "no more than `isize::MAX` apart" requirement.
372
- let a_offset = ImmTy :: from_uint ( a_offset. bytes ( ) , isize_layout) ;
373
- let b_offset = ImmTy :: from_uint ( b_offset. bytes ( ) , isize_layout) ;
374
- let ( val, overflowed, _ty) =
375
- self . overflowing_binary_op ( BinOp :: Sub , & a_offset, & b_offset) ?;
376
- if overflowed {
377
- throw_ub_format ! ( "Pointers were too far apart for {}" , intrinsic_name) ;
335
+ ( Ok ( ( a_alloc_id, a_offset, _) ) , Ok ( ( b_alloc_id, b_offset, _) ) ) => {
336
+ // Found allocation for both. They must be into the same allocation.
337
+ if a_alloc_id != b_alloc_id {
338
+ throw_ub_format ! (
339
+ "{} called on pointers into different allocations" ,
340
+ intrinsic_name
341
+ ) ;
342
+ }
343
+ // Use these offsets for distance calculation.
344
+ ( a_offset. bytes ( ) , b_offset. bytes ( ) )
378
345
}
379
-
380
- let pointee_layout = self . layout_of ( substs. type_at ( 0 ) ) ?;
381
- // This re-interprets an isize at ret_layout, but we already checked
382
- // that if ret_layout is usize, then the result must be non-negative.
383
- let val = ImmTy :: from_scalar ( val, ret_layout) ;
384
- let size = ImmTy :: from_int ( pointee_layout. size . bytes ( ) , ret_layout) ;
385
- self . exact_div ( & val, & size, dest) ?;
346
+ } ;
347
+
348
+ // Compute distance.
349
+ let distance = {
350
+ // The subtraction is always done in `isize` to enforce
351
+ // the "no more than `isize::MAX` apart" requirement.
352
+ let a_offset = ImmTy :: from_uint ( a_offset, isize_layout) ;
353
+ let b_offset = ImmTy :: from_uint ( b_offset, isize_layout) ;
354
+ let ( val, overflowed, _ty) =
355
+ self . overflowing_binary_op ( BinOp :: Sub , & a_offset, & b_offset) ?;
356
+ if overflowed {
357
+ throw_ub_format ! ( "pointers were too far apart for {}" , intrinsic_name) ;
386
358
}
359
+ val. to_machine_isize ( self ) ?
360
+ } ;
361
+
362
+ // Check that the range between them is dereferenceable ("in-bounds or one past the
363
+ // end of the same allocation"). This is like the check in ptr_offset_inbounds.
364
+ let min_ptr = if distance >= 0 { b } else { a } ;
365
+ self . check_ptr_access_align (
366
+ min_ptr,
367
+ Size :: from_bytes ( distance. unsigned_abs ( ) ) ,
368
+ Align :: ONE ,
369
+ CheckInAllocMsg :: OffsetFromTest ,
370
+ ) ?;
371
+
372
+ if intrinsic_name == sym:: ptr_offset_from_unsigned && distance < 0 {
373
+ throw_ub_format ! (
374
+ "{} called when first pointer has smaller offset than second: {} < {}" ,
375
+ intrinsic_name,
376
+ a_offset,
377
+ b_offset,
378
+ ) ;
387
379
}
380
+
381
+ // Perform division by size to compute return value.
382
+ let ret_layout = if intrinsic_name == sym:: ptr_offset_from_unsigned {
383
+ usize_layout
384
+ } else {
385
+ isize_layout
386
+ } ;
387
+ let pointee_layout = self . layout_of ( substs. type_at ( 0 ) ) ?;
388
+ // If ret_layout is unsigned, we checked that so is the distance, so we are good.
389
+ let val = ImmTy :: from_int ( distance, ret_layout) ;
390
+ let size = ImmTy :: from_int ( pointee_layout. size . bytes ( ) , ret_layout) ;
391
+ self . exact_div ( & val, & size, dest) ?;
388
392
}
389
393
390
394
sym:: transmute => {
@@ -575,11 +579,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
575
579
// memory between these pointers must be accessible. Note that we do not require the
576
580
// pointers to be properly aligned (unlike a read/write operation).
577
581
let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr } ;
578
- let size = offset_bytes. unsigned_abs ( ) ;
579
582
// This call handles checking for integer/null pointers.
580
583
self . check_ptr_access_align (
581
584
min_ptr,
582
- Size :: from_bytes ( size ) ,
585
+ Size :: from_bytes ( offset_bytes . unsigned_abs ( ) ) ,
583
586
Align :: ONE ,
584
587
CheckInAllocMsg :: PointerArithmeticTest ,
585
588
) ?;
0 commit comments