@@ -243,36 +243,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
243
243
let isize_layout = self . layout_of ( self . tcx . types . isize ) ?;
244
244
245
245
// 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 {
247
251
match ( self . ptr_try_get_alloc_id ( a) , self . ptr_try_get_alloc_id ( b) ) {
248
252
( 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 )
267
255
}
268
- // From here onwards, the pointers are definitely for different addresses
269
- // (or we can't determine their absolute address).
270
256
( Ok ( ( a_alloc_id, a_offset, _) ) , Ok ( ( b_alloc_id, b_offset, _) ) )
271
257
if a_alloc_id == b_alloc_id =>
272
258
{
273
259
// Found allocation for both, and it's the same.
274
260
// Use these offsets for distance calculation.
275
- ( a_offset. bytes ( ) , b_offset. bytes ( ) )
261
+ ( a_offset. bytes ( ) , b_offset. bytes ( ) , /*is_addr*/ false )
276
262
}
277
263
_ => {
278
264
// Not into the same allocation -- this is UB.
@@ -281,9 +267,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
281
267
name = intrinsic_name,
282
268
) ;
283
269
}
284
- } ;
270
+ }
271
+ } ;
285
272
286
- // Compute distance.
273
+ // Compute distance: a - b .
287
274
let dist = {
288
275
// Addresses are unsigned, so this is a `usize` computation. We have to do the
289
276
// overflow check separately anyway.
@@ -300,6 +287,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
300
287
fluent:: const_eval_offset_from_unsigned_overflow,
301
288
a_offset = a_offset,
302
289
b_offset = b_offset,
290
+ is_addr = is_addr,
303
291
) ;
304
292
}
305
293
// The signed form of the intrinsic allows this. If we interpret the
@@ -328,14 +316,23 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
328
316
}
329
317
} ;
330
318
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
337
327
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
+ } ) ?;
339
336
340
337
// Perform division by size to compute return value.
341
338
let ret_layout = if intrinsic_name == sym:: ptr_offset_from_unsigned {
@@ -582,27 +579,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
582
579
}
583
580
584
581
/// 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.
587
583
pub fn ptr_offset_inbounds (
588
584
& self ,
589
585
ptr : Pointer < Option < M :: Provenance > > ,
590
586
offset_bytes : i64 ,
591
587
) -> 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)
606
595
}
607
596
608
597
/// Copy `count*size_of::<T>()` many bytes from `*src` to `*dst`.
0 commit comments