@@ -313,78 +313,82 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
313313 let a = self . read_pointer ( & args[ 0 ] ) ?;
314314 let b = self . read_pointer ( & args[ 1 ] ) ?;
315315
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)
337326 }
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.
353330 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
358333 ) ;
359334 }
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 ( ) )
378345 }
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) ;
386358 }
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+ ) ;
387379 }
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) ?;
388392 }
389393
390394 sym:: transmute => {
@@ -575,11 +579,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
575579 // memory between these pointers must be accessible. Note that we do not require the
576580 // pointers to be properly aligned (unlike a read/write operation).
577581 let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr } ;
578- let size = offset_bytes. unsigned_abs ( ) ;
579582 // This call handles checking for integer/null pointers.
580583 self . check_ptr_access_align (
581584 min_ptr,
582- Size :: from_bytes ( size ) ,
585+ Size :: from_bytes ( offset_bytes . unsigned_abs ( ) ) ,
583586 Align :: ONE ,
584587 CheckInAllocMsg :: PointerArithmeticTest ,
585588 ) ?;
0 commit comments