@@ -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