Skip to content

Commit 58f2ede

Browse files
committed
interpret, ptr_offset_from: refactor and test too-far-apart check
1 parent 35a0617 commit 58f2ede

File tree

6 files changed

+152
-47
lines changed

6 files changed

+152
-47
lines changed

Diff for: compiler/rustc_const_eval/src/interpret/intrinsics.rs

+48-24
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::convert::TryFrom;
77
use rustc_hir::def_id::DefId;
88
use rustc_middle::mir::{
99
self,
10-
interpret::{ConstValue, GlobalId, InterpResult, Scalar},
10+
interpret::{ConstValue, GlobalId, InterpResult, PointerArithmetic, Scalar},
1111
BinOp,
1212
};
1313
use rustc_middle::ty;
@@ -328,15 +328,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
328328
// We managed to find a valid allocation for one pointer, but not the other.
329329
// That means they are definitely not pointing to the same allocation.
330330
throw_ub_format!(
331-
"{} called on pointers into different allocations",
331+
"`{}` called on pointers into different allocations",
332332
intrinsic_name
333333
);
334334
}
335335
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
336336
// Found allocation for both. They must be into the same allocation.
337337
if a_alloc_id != b_alloc_id {
338338
throw_ub_format!(
339-
"{} called on pointers into different allocations",
339+
"`{}` called on pointers into different allocations",
340340
intrinsic_name
341341
);
342342
}
@@ -346,47 +346,71 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
346346
};
347347

348348
// 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)?;
349+
let dist = {
350+
// Addresses are unsigned, so this is a `usize` computation. We have to do the
351+
// overflow check separately anyway.
352+
let (val, overflowed, _ty) = {
353+
let a_offset = ImmTy::from_uint(a_offset, usize_layout);
354+
let b_offset = ImmTy::from_uint(b_offset, usize_layout);
355+
self.overflowing_binary_op(BinOp::Sub, &a_offset, &b_offset)?
356+
};
356357
if overflowed {
357-
throw_ub_format!("pointers were too far apart for {}", intrinsic_name);
358+
// a < b
359+
if intrinsic_name == sym::ptr_offset_from_unsigned {
360+
throw_ub_format!(
361+
"`{}` called when first pointer has smaller offset than second: {} < {}",
362+
intrinsic_name,
363+
a_offset,
364+
b_offset,
365+
);
366+
}
367+
// The signed form of the intrinsic allows this. If we interpret the
368+
// difference as isize, we'll get the proper signed difference. If that
369+
// seems *positive*, they were more than isize::MAX apart.
370+
let dist = val.to_machine_isize(self)?;
371+
if dist >= 0 {
372+
throw_ub_format!(
373+
"`{}` called when first pointer is too far before second",
374+
intrinsic_name
375+
);
376+
}
377+
dist
378+
} else {
379+
// b >= a
380+
let dist = val.to_machine_isize(self)?;
381+
// If converting to isize produced a *negative* result, we had an overflow
382+
// because they were more than isize::MAX apart.
383+
if dist < 0 {
384+
throw_ub_format!(
385+
"`{}` called when first pointer is too far ahead of second",
386+
intrinsic_name
387+
);
388+
}
389+
dist
358390
}
359-
val.to_machine_isize(self)?
360391
};
361392

362393
// Check that the range between them is dereferenceable ("in-bounds or one past the
363394
// end of the same allocation"). This is like the check in ptr_offset_inbounds.
364-
let min_ptr = if distance >= 0 { b } else { a };
395+
let min_ptr = if dist >= 0 { b } else { a };
365396
self.check_ptr_access_align(
366397
min_ptr,
367-
Size::from_bytes(distance.unsigned_abs()),
398+
Size::from_bytes(dist.unsigned_abs()),
368399
Align::ONE,
369400
CheckInAllocMsg::OffsetFromTest,
370401
)?;
371402

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-
);
379-
}
380-
381403
// Perform division by size to compute return value.
382404
let ret_layout = if intrinsic_name == sym::ptr_offset_from_unsigned {
405+
assert!(0 <= dist && dist <= self.machine_isize_max());
383406
usize_layout
384407
} else {
408+
assert!(self.machine_isize_min() <= dist && dist <= self.machine_isize_max());
385409
isize_layout
386410
};
387411
let pointee_layout = self.layout_of(substs.type_at(0))?;
388412
// 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);
413+
let val = ImmTy::from_int(dist, ret_layout);
390414
let size = ImmTy::from_int(pointee_layout.size.bytes(), ret_layout);
391415
self.exact_div(&val, &size, dest)?;
392416
}

Diff for: library/core/src/ptr/const_ptr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ impl<T: ?Sized> *const T {
726726
/// }
727727
///
728728
/// // This would be incorrect, as the pointers are not correctly ordered:
729-
/// // ptr1.offset_from(ptr2)
729+
/// // ptr1.sub_ptr(ptr2)
730730
/// ```
731731
#[unstable(feature = "ptr_sub_ptr", issue = "95892")]
732732
#[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]

Diff for: src/test/ui/const-ptr/forbidden_slices.32bit.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ error[E0080]: could not evaluate static initializer
222222
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
223223
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
224224
| |
225-
| ptr_offset_from_unsigned called on pointers into different allocations
225+
| `ptr_offset_from_unsigned` called on pointers into different allocations
226226
| inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
227227
|
228228
::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
@@ -241,7 +241,7 @@ error[E0080]: could not evaluate static initializer
241241
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
242242
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
243243
| |
244-
| ptr_offset_from_unsigned called on pointers into different allocations
244+
| `ptr_offset_from_unsigned` called on pointers into different allocations
245245
| inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
246246
|
247247
::: $SRC_DIR/core/src/slice/raw.rs:LL:COL

Diff for: src/test/ui/const-ptr/forbidden_slices.64bit.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ error[E0080]: could not evaluate static initializer
222222
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
223223
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
224224
| |
225-
| ptr_offset_from_unsigned called on pointers into different allocations
225+
| `ptr_offset_from_unsigned` called on pointers into different allocations
226226
| inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
227227
|
228228
::: $SRC_DIR/core/src/slice/raw.rs:LL:COL
@@ -241,7 +241,7 @@ error[E0080]: could not evaluate static initializer
241241
LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) }
242242
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
243243
| |
244-
| ptr_offset_from_unsigned called on pointers into different allocations
244+
| `ptr_offset_from_unsigned` called on pointers into different allocations
245245
| inside `ptr::const_ptr::<impl *const u32>::sub_ptr` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
246246
|
247247
::: $SRC_DIR/core/src/slice/raw.rs:LL:COL

Diff for: src/test/ui/consts/offset_from_ub.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![feature(core_intrinsics)]
33

44
use std::intrinsics::{ptr_offset_from, ptr_offset_from_unsigned};
5+
use std::ptr;
56

67
#[repr(C)]
78
struct Struct {
@@ -75,9 +76,21 @@ pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
7576
let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
7677
let uninit2 = std::mem::MaybeUninit::<Struct>::uninit();
7778
let field_ptr: *const Struct = &uninit2 as *const _ as *const Struct;
78-
let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }; //~ERROR evaluation of constant value failed
79+
unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) } //~ERROR evaluation of constant value failed
7980
//~| pointers into different allocations
80-
offset as usize
81+
};
82+
83+
pub const TOO_FAR_APART1: isize = {
84+
let ptr1 = ptr::null::<u8>();
85+
let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42);
86+
unsafe { ptr_offset_from(ptr2, ptr1) } //~ERROR evaluation of constant value failed
87+
//~| too far ahead
88+
};
89+
pub const TOO_FAR_APART2: isize = {
90+
let ptr1 = ptr::null::<u8>();
91+
let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42);
92+
unsafe { ptr_offset_from(ptr1, ptr2) } //~ERROR evaluation of constant value failed
93+
//~| too far before
8194
};
8295

8396
const WRONG_ORDER_UNSIGNED: usize = {
@@ -86,5 +99,27 @@ const WRONG_ORDER_UNSIGNED: usize = {
8699
unsafe { ptr_offset_from_unsigned(p, p.add(2) ) } //~ERROR evaluation of constant value failed
87100
//~| first pointer has smaller offset than second: 0 < 8
88101
};
102+
pub const TOO_FAR_APART_UNSIGNED: usize = {
103+
let ptr1 = ptr::null::<u8>();
104+
let ptr2 = ptr1.wrapping_add(isize::MAX as usize + 42);
105+
// This would fit into a `usize` but we still don't allow it.
106+
unsafe { ptr_offset_from_unsigned(ptr2, ptr1) } //~ERROR evaluation of constant value failed
107+
//~| too far ahead
108+
};
109+
110+
// These do NOT complain that pointers are too far apart; they pass that check (to then fail the
111+
// next one).
112+
pub const OFFSET_VERY_FAR1: isize = {
113+
let ptr1 = ptr::null::<u8>();
114+
let ptr2 = ptr1.wrapping_offset(isize::MAX);
115+
unsafe { ptr2.offset_from(ptr1) }
116+
//~^ inside
117+
};
118+
pub const OFFSET_VERY_FAR2: isize = {
119+
let ptr1 = ptr::null::<u8>();
120+
let ptr2 = ptr1.wrapping_offset(isize::MAX);
121+
unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
122+
//~^ inside
123+
};
89124

90125
fn main() {}

Diff for: src/test/ui/consts/offset_from_ub.stderr

+62-16
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,117 @@
11
error[E0080]: evaluation of constant value failed
2-
--> $DIR/offset_from_ub.rs:17:27
2+
--> $DIR/offset_from_ub.rs:18:27
33
|
44
LL | let offset = unsafe { ptr_offset_from(field_ptr, base_ptr) };
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from called on pointers into different allocations
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on pointers into different allocations
66

77
error[E0080]: evaluation of constant value failed
88
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
99
|
1010
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
| |
13-
| ptr_offset_from called on pointers into different allocations
13+
| `ptr_offset_from` called on pointers into different allocations
1414
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
1515
|
16-
::: $DIR/offset_from_ub.rs:23:14
16+
::: $DIR/offset_from_ub.rs:24:14
1717
|
1818
LL | unsafe { (42 as *const u8).offset_from(&5u8) as usize }
19-
| ----------------------------------- inside `NOT_PTR` at $DIR/offset_from_ub.rs:23:14
19+
| ----------------------------------- inside `NOT_PTR` at $DIR/offset_from_ub.rs:24:14
2020

2121
error[E0080]: evaluation of constant value failed
22-
--> $DIR/offset_from_ub.rs:30:14
22+
--> $DIR/offset_from_ub.rs:31:14
2323
|
2424
LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) }
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder
2626

2727
error[E0080]: evaluation of constant value failed
28-
--> $DIR/offset_from_ub.rs:36:14
28+
--> $DIR/offset_from_ub.rs:37:14
2929
|
3030
LL | unsafe { ptr_offset_from(ptr, ptr) }
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
3232

3333
error[E0080]: evaluation of constant value failed
34-
--> $DIR/offset_from_ub.rs:43:14
34+
--> $DIR/offset_from_ub.rs:44:14
3535
|
3636
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: 0x8[noalloc] is a dangling pointer (it has no provenance)
3838

3939
error[E0080]: evaluation of constant value failed
40-
--> $DIR/offset_from_ub.rs:52:14
40+
--> $DIR/offset_from_ub.rs:53:14
4141
|
4242
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
4343
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc18 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
4444

4545
error[E0080]: evaluation of constant value failed
46-
--> $DIR/offset_from_ub.rs:61:14
46+
--> $DIR/offset_from_ub.rs:62:14
4747
|
4848
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
4949
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc21 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
5050

5151
error[E0080]: evaluation of constant value failed
52-
--> $DIR/offset_from_ub.rs:69:14
52+
--> $DIR/offset_from_ub.rs:70:14
5353
|
5454
LL | unsafe { ptr_offset_from(end_ptr, end_ptr) }
5555
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds offset_from: alloc24 has size 4, so pointer at offset 10 is out-of-bounds
5656

5757
error[E0080]: evaluation of constant value failed
58-
--> $DIR/offset_from_ub.rs:78:27
58+
--> $DIR/offset_from_ub.rs:79:14
5959
|
60-
LL | let offset = unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) };
61-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned called on pointers into different allocations
60+
LL | unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
6262

6363
error[E0080]: evaluation of constant value failed
6464
--> $DIR/offset_from_ub.rs:86:14
6565
|
66+
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far ahead of second
68+
69+
error[E0080]: evaluation of constant value failed
70+
--> $DIR/offset_from_ub.rs:92:14
71+
|
72+
LL | unsafe { ptr_offset_from(ptr1, ptr2) }
73+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
74+
75+
error[E0080]: evaluation of constant value failed
76+
--> $DIR/offset_from_ub.rs:99:14
77+
|
6678
LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
67-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ptr_offset_from_unsigned called when first pointer has smaller offset than second: 0 < 8
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8
80+
81+
error[E0080]: evaluation of constant value failed
82+
--> $DIR/offset_from_ub.rs:106:14
83+
|
84+
LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
85+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second
86+
87+
error[E0080]: evaluation of constant value failed
88+
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
89+
|
90+
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
91+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
92+
| |
93+
| out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
94+
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
95+
|
96+
::: $DIR/offset_from_ub.rs:115:14
97+
|
98+
LL | unsafe { ptr2.offset_from(ptr1) }
99+
| ---------------------- inside `OFFSET_VERY_FAR1` at $DIR/offset_from_ub.rs:115:14
100+
101+
error[E0080]: evaluation of constant value failed
102+
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
103+
|
104+
LL | unsafe { intrinsics::ptr_offset_from(self, origin) }
105+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
106+
| |
107+
| out-of-bounds offset_from: null pointer is a dangling pointer (it has no provenance)
108+
| inside `ptr::const_ptr::<impl *const u8>::offset_from` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
109+
|
110+
::: $DIR/offset_from_ub.rs:121:14
111+
|
112+
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
113+
| ----------------------------------------- inside `OFFSET_VERY_FAR2` at $DIR/offset_from_ub.rs:121:14
68114

69-
error: aborting due to 10 previous errors
115+
error: aborting due to 15 previous errors
70116

71117
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)