@@ -243,36 +243,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
243243 let isize_layout = self . layout_of ( self . tcx . types . isize ) ?;
244244
245245 // Get offsets for both that are at least relative to the same base.
246- let ( a_offset, b_offset) =
246+ // With `OFFSET_IS_ADDR` this is trivial; without it we need either
247+ // two integers or two pointers into the same allocation.
248+ let ( a_offset, b_offset, is_addr) = if M :: Provenance :: OFFSET_IS_ADDR {
249+ ( a. addr ( ) . bytes ( ) , b. addr ( ) . bytes ( ) , /*is_addr*/ true )
250+ } else {
247251 match ( self . ptr_try_get_alloc_id ( a) , self . ptr_try_get_alloc_id ( b) ) {
248252 ( Err ( a) , Err ( b) ) => {
249- // Neither pointer points to an allocation.
250- // This is okay only if they are the same.
251- if a != b {
252- // We'd catch this below in the "dereferenceable" check, but
253- // show a nicer error for this particular case.
254- throw_ub_custom ! (
255- fluent:: const_eval_offset_from_different_integers,
256- name = intrinsic_name,
257- ) ;
258- }
259- // This will always return 0.
260- ( a, b)
261- }
262- _ if M :: Provenance :: OFFSET_IS_ADDR && a. addr ( ) == b. addr ( ) => {
263- // At least one of the pointers has provenance, but they also point to
264- // the same address so it doesn't matter; this is fine. `(0, 0)` means
265- // we pass all the checks below and return 0.
266- ( 0 , 0 )
253+ // Neither pointer points to an allocation, so they are both absolute.
254+ ( a, b, /*is_addr*/ true )
267255 }
268- // From here onwards, the pointers are definitely for different addresses
269- // (or we can't determine their absolute address).
270256 ( Ok ( ( a_alloc_id, a_offset, _) ) , Ok ( ( b_alloc_id, b_offset, _) ) )
271257 if a_alloc_id == b_alloc_id =>
272258 {
273259 // Found allocation for both, and it's the same.
274260 // Use these offsets for distance calculation.
275- ( a_offset. bytes ( ) , b_offset. bytes ( ) )
261+ ( a_offset. bytes ( ) , b_offset. bytes ( ) , /*is_addr*/ false )
276262 }
277263 _ => {
278264 // Not into the same allocation -- this is UB.
@@ -281,9 +267,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
281267 name = intrinsic_name,
282268 ) ;
283269 }
284- } ;
270+ }
271+ } ;
285272
286- // Compute distance.
273+ // Compute distance: a - b .
287274 let dist = {
288275 // Addresses are unsigned, so this is a `usize` computation. We have to do the
289276 // overflow check separately anyway.
@@ -300,6 +287,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
300287 fluent:: const_eval_offset_from_unsigned_overflow,
301288 a_offset = a_offset,
302289 b_offset = b_offset,
290+ is_addr = is_addr,
303291 ) ;
304292 }
305293 // The signed form of the intrinsic allows this. If we interpret the
@@ -328,14 +316,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
328316 }
329317 } ;
330318
331- // Check that the range between them is dereferenceable ("in-bounds or one past the
332- // end of the same allocation"). This is like the check in ptr_offset_inbounds.
333- let min_ptr = if dist >= 0 { b } else { a } ;
334- self . check_ptr_access (
335- min_ptr,
336- Size :: from_bytes ( dist. unsigned_abs ( ) ) ,
319+ // Check that the memory between them is dereferenceable at all, starting from the
320+ // base pointer: `dist` is `a - b`, so it is based on `b`.
321+ self . check_ptr_access_signed ( b, dist, CheckInAllocMsg :: OffsetFromTest ) ?;
322+ // Then check that this is also dereferenceable from `a`. This ensures that they are
323+ // derived from the same allocation.
324+ self . check_ptr_access_signed (
325+ a,
326+ dist. checked_neg ( ) . unwrap ( ) , // i64::MIN is impossible as no allocation can be that large
337327 CheckInAllocMsg :: OffsetFromTest ,
338- ) ?;
328+ )
329+ . map_err ( |_| {
330+ // Make the error more specific.
331+ err_ub_custom ! (
332+ fluent:: const_eval_offset_from_different_allocations,
333+ name = intrinsic_name,
334+ )
335+ } ) ?;
339336
340337 // Perform division by size to compute return value.
341338 let ret_layout = if intrinsic_name == sym:: ptr_offset_from_unsigned {
@@ -582,27 +579,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
582579 }
583580
584581 /// Offsets a pointer by some multiple of its type, returning an error if the pointer leaves its
585- /// allocation. For integer pointers, we consider each of them their own tiny allocation of size
586- /// 0, so offset-by-0 (and only 0) is okay -- except that null cannot be offset by _any_ value.
582+ /// allocation.
587583 pub fn ptr_offset_inbounds (
588584 & self ,
589585 ptr : Pointer < Option < M :: Provenance > > ,
590586 offset_bytes : i64 ,
591587 ) -> InterpResult < ' tcx , Pointer < Option < M :: Provenance > > > {
592- // The offset being in bounds cannot rely on "wrapping around" the address space.
593- // So, first rule out overflows in the pointer arithmetic.
594- let offset_ptr = ptr. signed_offset ( offset_bytes, self ) ?;
595- // ptr and offset_ptr must be in bounds of the same allocated object. This means all of the
596- // memory between these pointers must be accessible. Note that we do not require the
597- // pointers to be properly aligned (unlike a read/write operation).
598- let min_ptr = if offset_bytes >= 0 { ptr } else { offset_ptr } ;
599- // This call handles checking for integer/null pointers.
600- self . check_ptr_access (
601- min_ptr,
602- Size :: from_bytes ( offset_bytes. unsigned_abs ( ) ) ,
603- CheckInAllocMsg :: PointerArithmeticTest ,
604- ) ?;
605- Ok ( offset_ptr)
588+ // We first compute the pointer with overflow checks, to get a specific error for when it
589+ // overflows (though technically this is redundant with the following inbounds check).
590+ let result = ptr. signed_offset ( offset_bytes, self ) ?;
591+ // The offset must be in bounds starting from `ptr`.
592+ self . check_ptr_access_signed ( ptr, offset_bytes, CheckInAllocMsg :: PointerArithmeticTest ) ?;
593+ // Done.
594+ Ok ( result)
606595 }
607596
608597 /// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
0 commit comments