Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8f8766a

Browse files
committedMay 10, 2024
offset_from intrinsic: always allow pointers to point to the same address
1 parent 353c636 commit 8f8766a

File tree

6 files changed

+59
-54
lines changed

6 files changed

+59
-54
lines changed
 

‎compiler/rustc_const_eval/src/interpret/intrinsics.rs

+18-15
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_target::abi::Size;
1717
use super::{
1818
memory::MemoryKind, util::ensure_monomorphic_enough, Allocation, CheckInAllocMsg,
1919
ConstAllocation, GlobalId, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, Pointer,
20-
PointerArithmetic, Scalar,
20+
PointerArithmetic, Provenance, Scalar,
2121
};
2222

2323
use crate::fluent_generated as fluent;
@@ -256,25 +256,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
256256
// This will always return 0.
257257
(a, b)
258258
}
259-
(Err(_), _) | (_, Err(_)) => {
260-
// We managed to find a valid allocation for one pointer, but not the other.
261-
// That means they are definitely not pointing to the same allocation.
259+
_ if M::Provenance::OFFSET_IS_ADDR && a.addr() == b.addr() => {
260+
// At least one of the pointers has provenance, but they also point to
261+
// the same address so it doesn't matter; this is fine. `(0, 0)` means
262+
// we pass all the checks below and return 0.
263+
(0, 0)
264+
}
265+
// From here onwards, the pointers are definitely for different addresses
266+
// (or we can't determine their absolute address).
267+
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _)))
268+
if a_alloc_id == b_alloc_id =>
269+
{
270+
// Found allocation for both, and it's the same.
271+
// Use these offsets for distance calculation.
272+
(a_offset.bytes(), b_offset.bytes())
273+
}
274+
_ => {
275+
// Not into the same allocation -- this is UB.
262276
throw_ub_custom!(
263277
fluent::const_eval_offset_from_different_allocations,
264278
name = intrinsic_name,
265279
);
266280
}
267-
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
268-
// Found allocation for both. They must be into the same allocation.
269-
if a_alloc_id != b_alloc_id {
270-
throw_ub_custom!(
271-
fluent::const_eval_offset_from_different_allocations,
272-
name = intrinsic_name,
273-
);
274-
}
275-
// Use these offsets for distance calculation.
276-
(a_offset.bytes(), b_offset.bytes())
277-
}
278281
};
279282

280283
// Compute distance.

‎library/core/src/ptr/const_ptr.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -679,9 +679,9 @@ impl<T: ?Sized> *const T {
679679
///
680680
/// * `self` and `origin` must either
681681
///
682+
/// * point to the same address, or
682683
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
683-
/// the two pointers must be either empty or in bounds of that object. (See below for an example.)
684-
/// * or both be derived from an integer literal/constant, and point to the same address.
684+
/// the two pointers must be in bounds of that object. (See below for an example.)
685685
///
686686
/// * The distance between the pointers, in bytes, must be an exact multiple
687687
/// of the size of `T`.
@@ -744,14 +744,14 @@ impl<T: ?Sized> *const T {
744744
/// let ptr1 = Box::into_raw(Box::new(0u8)) as *const u8;
745745
/// let ptr2 = Box::into_raw(Box::new(1u8)) as *const u8;
746746
/// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize);
747-
/// // Make ptr2_other an "alias" of ptr2, but derived from ptr1.
748-
/// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff);
747+
/// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1.
748+
/// let ptr2_other = (ptr1 as *const u8).wrapping_offset(diff).wrapping_offset(1);
749749
/// assert_eq!(ptr2 as usize, ptr2_other as usize);
750750
/// // Since ptr2_other and ptr2 are derived from pointers to different objects,
751751
/// // computing their offset is undefined behavior, even though
752-
/// // they point to the same address!
752+
/// // they point to addresses that are in-bounds of the same object!
753753
/// unsafe {
754-
/// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior
754+
/// let one = ptr2_other.offset_from(ptr2); // Undefined Behavior
755755
/// }
756756
/// ```
757757
#[stable(feature = "ptr_offset_from", since = "1.47.0")]

‎library/core/src/ptr/mut_ptr.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -907,9 +907,9 @@ impl<T: ?Sized> *mut T {
907907
///
908908
/// * `self` and `origin` must either
909909
///
910+
/// * point to the same address, or
910911
/// * both be *derived from* a pointer to the same [allocated object], and the memory range between
911-
/// the two pointers must be either empty or in bounds of that object. (See below for an example.)
912-
/// * or both be derived from an integer literal/constant, and point to the same address.
912+
/// the two pointers must be in bounds of that object. (See below for an example.)
913913
///
914914
/// * The distance between the pointers, in bytes, must be an exact multiple
915915
/// of the size of `T`.
@@ -972,14 +972,14 @@ impl<T: ?Sized> *mut T {
972972
/// let ptr1 = Box::into_raw(Box::new(0u8));
973973
/// let ptr2 = Box::into_raw(Box::new(1u8));
974974
/// let diff = (ptr2 as isize).wrapping_sub(ptr1 as isize);
975-
/// // Make ptr2_other an "alias" of ptr2, but derived from ptr1.
976-
/// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff);
975+
/// // Make ptr2_other an "alias" of ptr2.add(1), but derived from ptr1.
976+
/// let ptr2_other = (ptr1 as *mut u8).wrapping_offset(diff).wrapping_offset(1);
977977
/// assert_eq!(ptr2 as usize, ptr2_other as usize);
978978
/// // Since ptr2_other and ptr2 are derived from pointers to different objects,
979979
/// // computing their offset is undefined behavior, even though
980-
/// // they point to the same address!
980+
/// // they point to addresses that are in-bounds of the same object!
981981
/// unsafe {
982-
/// let zero = ptr2_other.offset_from(ptr2); // Undefined Behavior
982+
/// let one = ptr2_other.offset_from(ptr2); // Undefined Behavior
983983
/// }
984984
/// ```
985985
#[stable(feature = "ptr_offset_from", since = "1.47.0")]

‎src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs

-3
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ fn test_ptr(ptr: *mut ()) {
3939
// Distance.
4040
let ptr = ptr.cast::<i32>();
4141
ptr.offset_from(ptr);
42-
/*
43-
FIXME: this is disabled for now as these cases are not yet allowed.
4442
// Distance from other "bad" pointers that have the same address, but different provenance. Some
4543
// of this is library UB, but we don't want it to be language UB since that would violate
4644
// provenance monotonicity: if we allow computing the distance between two ptrs with no
@@ -54,6 +52,5 @@ fn test_ptr(ptr: *mut ()) {
5452
// - Distance from use-after-free pointer
5553
drop(b);
5654
ptr.offset_from(other_ptr.with_addr(ptr.addr()));
57-
*/
5855
}
5956
}

‎tests/ui/consts/offset_from_ub.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,6 @@ pub const NOT_MULTIPLE_OF_SIZE: isize = {
3232
//~| 1_isize cannot be divided by 2_isize without remainder
3333
};
3434

35-
pub const OFFSET_FROM_NULL: isize = {
36-
let ptr = 0 as *const u8;
37-
// Null isn't special for zero-sized "accesses" (i.e., the range between the two pointers)
38-
unsafe { ptr_offset_from(ptr, ptr) }
39-
};
40-
4135
pub const DIFFERENT_INT: isize = { // offset_from with two different integers: like DIFFERENT_ALLOC
4236
let ptr1 = 8 as *const u8;
4337
let ptr2 = 16 as *const u8;
@@ -63,14 +57,6 @@ const OUT_OF_BOUNDS_2: isize = {
6357
//~| pointer to 10 bytes starting at offset 0 is out-of-bounds
6458
};
6559

66-
const OUT_OF_BOUNDS_SAME: isize = {
67-
let start_ptr = &4 as *const _ as *const u8;
68-
let length = 10;
69-
let end_ptr = (start_ptr).wrapping_add(length);
70-
// Out-of-bounds is fine as long as the range between the pointers is empty.
71-
unsafe { ptr_offset_from(end_ptr, end_ptr) }
72-
};
73-
7460
pub const DIFFERENT_ALLOC_UNSIGNED: usize = {
7561
let uninit = std::mem::MaybeUninit::<Struct>::uninit();
7662
let base_ptr: *const Struct = &uninit as *const _ as *const Struct;
@@ -122,4 +108,23 @@ pub const OFFSET_VERY_FAR2: isize = {
122108
//~^ inside
123109
};
124110

111+
// If the pointers are the same, OOB/null/UAF is fine.
112+
pub const OFFSET_FROM_NULL_SAME: isize = {
113+
let ptr = 0 as *const u8;
114+
unsafe { ptr_offset_from(ptr, ptr) }
115+
};
116+
const OUT_OF_BOUNDS_SAME: isize = {
117+
let start_ptr = &4 as *const _ as *const u8;
118+
let length = 10;
119+
let end_ptr = (start_ptr).wrapping_add(length);
120+
unsafe { ptr_offset_from(end_ptr, end_ptr) }
121+
};
122+
const UAF_SAME: isize = {
123+
let uaf_ptr = {
124+
let x = 0;
125+
&x as *const i32
126+
};
127+
unsafe { ptr_offset_from(uaf_ptr, uaf_ptr) }
128+
};
129+
125130
fn main() {}

‎tests/ui/consts/offset_from_ub.stderr

+10-10
Original file line numberDiff line numberDiff line change
@@ -24,49 +24,49 @@ LL | unsafe { ptr_offset_from(field_ptr, base_ptr as *const u16) }
2424
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ exact_div: 1_isize cannot be divided by 2_isize without remainder
2525

2626
error[E0080]: evaluation of constant value failed
27-
--> $DIR/offset_from_ub.rs:44:14
27+
--> $DIR/offset_from_ub.rs:38:14
2828
|
2929
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
3030
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called on different pointers without provenance (i.e., without an associated allocation)
3131

3232
error[E0080]: evaluation of constant value failed
33-
--> $DIR/offset_from_ub.rs:53:14
33+
--> $DIR/offset_from_ub.rs:47:14
3434
|
3535
LL | unsafe { ptr_offset_from(end_ptr, start_ptr) }
3636
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC0 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
3737

3838
error[E0080]: evaluation of constant value failed
39-
--> $DIR/offset_from_ub.rs:62:14
39+
--> $DIR/offset_from_ub.rs:56:14
4040
|
4141
LL | unsafe { ptr_offset_from(start_ptr, end_ptr) }
4242
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds `offset_from`: ALLOC1 has size 4, so pointer to 10 bytes starting at offset 0 is out-of-bounds
4343

4444
error[E0080]: evaluation of constant value failed
45-
--> $DIR/offset_from_ub.rs:79:14
45+
--> $DIR/offset_from_ub.rs:65:14
4646
|
4747
LL | unsafe { ptr_offset_from_unsigned(field_ptr, base_ptr) }
4848
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called on pointers into different allocations
4949

5050
error[E0080]: evaluation of constant value failed
51-
--> $DIR/offset_from_ub.rs:86:14
51+
--> $DIR/offset_from_ub.rs:72:14
5252
|
5353
LL | unsafe { ptr_offset_from(ptr2, ptr1) }
5454
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far ahead of second
5555

5656
error[E0080]: evaluation of constant value failed
57-
--> $DIR/offset_from_ub.rs:92:14
57+
--> $DIR/offset_from_ub.rs:78:14
5858
|
5959
LL | unsafe { ptr_offset_from(ptr1, ptr2) }
6060
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from` called when first pointer is too far before second
6161

6262
error[E0080]: evaluation of constant value failed
63-
--> $DIR/offset_from_ub.rs:99:14
63+
--> $DIR/offset_from_ub.rs:85:14
6464
|
6565
LL | unsafe { ptr_offset_from_unsigned(p, p.add(2) ) }
6666
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: 0 < 8
6767

6868
error[E0080]: evaluation of constant value failed
69-
--> $DIR/offset_from_ub.rs:106:14
69+
--> $DIR/offset_from_ub.rs:92:14
7070
|
7171
LL | unsafe { ptr_offset_from_unsigned(ptr2, ptr1) }
7272
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `ptr_offset_from_unsigned` called when first pointer is too far ahead of second
@@ -79,7 +79,7 @@ error[E0080]: evaluation of constant value failed
7979
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
8080
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
8181
note: inside `OFFSET_VERY_FAR1`
82-
--> $DIR/offset_from_ub.rs:115:14
82+
--> $DIR/offset_from_ub.rs:101:14
8383
|
8484
LL | unsafe { ptr2.offset_from(ptr1) }
8585
| ^^^^^^^^^^^^^^^^^^^^^^
@@ -92,7 +92,7 @@ error[E0080]: evaluation of constant value failed
9292
note: inside `std::ptr::const_ptr::<impl *const u8>::offset_from`
9393
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
9494
note: inside `OFFSET_VERY_FAR2`
95-
--> $DIR/offset_from_ub.rs:121:14
95+
--> $DIR/offset_from_ub.rs:107:14
9696
|
9797
LL | unsafe { ptr1.offset_from(ptr2.wrapping_offset(1)) }
9898
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)
Failed to load comments.