Skip to content

Commit 6d246e4

Browse files
committed
Add precondition checks to ptr::offset, ptr::add, ptr::sub
1 parent 7caad69 commit 6d246e4

13 files changed

Lines changed: 277 additions & 43 deletions

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

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,36 @@ impl<T: ?Sized> *const T {
395395
where
396396
T:Sized,
397397
{
398+
#[inline]
399+
constfnruntime_offset_nowrap(this:*const(),count:isize,size:usize) -> bool{
400+
#[inline]
401+
fnruntime(this:*const(),count:isize,size:usize) -> bool{
402+
// We know `size <= isize::MAX` so the `as` cast here is not lossy.
403+
letSome(byte_offset) = count.checked_mul(size asisize)else{
404+
returnfalse;
405+
};
406+
let(_, overflow) = this.addr().overflowing_add_signed(byte_offset);
407+
!overflow
408+
}
409+
410+
constfncomptime(_:*const(), _:isize, _:usize) -> bool{
411+
true
412+
}
413+
414+
// We can use const_eval_select here because this is only for UB checks.
415+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
416+
}
417+
418+
ub_checks::assert_unsafe_precondition!(
419+
check_language_ub,
420+
"ptr::offset requires the address calculation to not overflow",
421+
(
422+
this:*const() = selfas*const(),
423+
count:isize = count,
424+
size:usize = size_of::<T>(),
425+
) => runtime_offset_nowrap(this, count, size)
426+
);
427+
398428
// SAFETY: the caller must uphold the safety contract for `offset`.
399429
unsafe{ intrinsics::offset(self, count)}
400430
}
@@ -726,7 +756,6 @@ impl<T: ?Sized> *const T {
726756
true
727757
}
728758

729-
#[allow(unused_unsafe)]
730759
intrinsics::const_eval_select((this, origin), comptime, runtime)
731760
}
732761

@@ -858,6 +887,34 @@ impl<T: ?Sized> *const T {
858887
where
859888
T:Sized,
860889
{
890+
#[inline]
891+
constfnruntime_add_nowrap(this:*const(),count:usize,size:usize) -> bool{
892+
#[inline]
893+
fnruntime(this:*const(),count:usize,size:usize) -> bool{
894+
letSome(byte_offset) = count.checked_mul(size)else{
895+
returnfalse;
896+
};
897+
let(_, overflow) = this.addr().overflowing_add(byte_offset);
898+
byte_offset <= (isize::MAXasusize) && !overflow
899+
}
900+
901+
constfncomptime(_:*const(), _:usize, _:usize) -> bool{
902+
true
903+
}
904+
905+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
906+
}
907+
908+
ub_checks::assert_unsafe_precondition!(
909+
check_language_ub,
910+
"ptr::add requires that the address calculation does not overflow",
911+
(
912+
this:*const() = selfas*const(),
913+
count:usize = count,
914+
size:usize = size_of::<T>(),
915+
) => runtime_add_nowrap(this, count, size)
916+
);
917+
861918
// SAFETY: the caller must uphold the safety contract for `offset`.
862919
unsafe{ intrinsics::offset(self, count)}
863920
}
@@ -936,14 +993,41 @@ impl<T: ?Sized> *const T {
936993
where
937994
T:Sized,
938995
{
996+
#[inline]
997+
constfnruntime_sub_nowrap(this:*const(),count:usize,size:usize) -> bool{
998+
#[inline]
999+
fnruntime(this:*const(),count:usize,size:usize) -> bool{
1000+
letSome(byte_offset) = count.checked_mul(size)else{
1001+
returnfalse;
1002+
};
1003+
byte_offset <= (isize::MAXasusize) && this.addr() >= byte_offset
1004+
}
1005+
1006+
constfncomptime(_:*const(), _:usize, _:usize) -> bool{
1007+
true
1008+
}
1009+
1010+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1011+
}
1012+
1013+
ub_checks::assert_unsafe_precondition!(
1014+
check_language_ub,
1015+
"ptr::sub requires that the address calculation does not overflow",
1016+
(
1017+
this:*const() = selfas*const(),
1018+
count:usize = count,
1019+
size:usize = size_of::<T>(),
1020+
) => runtime_sub_nowrap(this, count, size)
1021+
);
1022+
9391023
ifT::IS_ZST{
9401024
// Pointer arithmetic does nothing when the pointee is a ZST.
9411025
self
9421026
}else{
9431027
// SAFETY: the caller must uphold the safety contract for `offset`.
9441028
// Because the pointee is *not* a ZST, that means that `count` is
9451029
// at most `isize::MAX`, and thus the negation cannot overflow.
946-
unsafe{self.offset((count asisize).unchecked_neg())}
1030+
unsafe{intrinsics::offset(self, intrinsics::unchecked_sub(0,count asisize))}
9471031
}
9481032
}
9491033

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

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,37 @@ impl<T: ?Sized> *mut T {
393393
where
394394
T:Sized,
395395
{
396+
#[inline]
397+
constfnruntime_offset_nowrap(this:*const(),count:isize,size:usize) -> bool{
398+
#[inline]
399+
fnruntime(this:*const(),count:isize,size:usize) -> bool{
400+
// `size` is the size of a Rust type, so we know that
401+
// `size <= isize::MAX` and thus `as` cast here is not lossy.
402+
letSome(byte_offset) = count.checked_mul(size asisize)else{
403+
returnfalse;
404+
};
405+
let(_, overflow) = this.addr().overflowing_add_signed(byte_offset);
406+
!overflow
407+
}
408+
409+
constfncomptime(_:*const(), _:isize, _:usize) -> bool{
410+
true
411+
}
412+
413+
// We can use const_eval_select here because this is only for UB checks.
414+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
415+
}
416+
417+
ub_checks::assert_unsafe_precondition!(
418+
check_language_ub,
419+
"ptr::offset requires the address calculation to not overflow",
420+
(
421+
this:*const() = selfas*const(),
422+
count:isize = count,
423+
size:usize = size_of::<T>(),
424+
) => runtime_offset_nowrap(this, count, size)
425+
);
426+
396427
// SAFETY: the caller must uphold the safety contract for `offset`.
397428
// The obtained pointer is valid for writes since the caller must
398429
// guarantee that it points to the same allocated object as `self`.
@@ -940,6 +971,34 @@ impl<T: ?Sized> *mut T {
940971
where
941972
T:Sized,
942973
{
974+
#[inline]
975+
constfnruntime_add_nowrap(this:*const(),count:usize,size:usize) -> bool{
976+
#[inline]
977+
fnruntime(this:*const(),count:usize,size:usize) -> bool{
978+
letSome(byte_offset) = count.checked_mul(size)else{
979+
returnfalse;
980+
};
981+
let(_, overflow) = this.addr().overflowing_add(byte_offset);
982+
byte_offset <= (isize::MAXasusize) && !overflow
983+
}
984+
985+
constfncomptime(_:*const(), _:usize, _:usize) -> bool{
986+
true
987+
}
988+
989+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
990+
}
991+
992+
ub_checks::assert_unsafe_precondition!(
993+
check_language_ub,
994+
"ptr::add requires that the address calculation does not overflow",
995+
(
996+
this:*const() = selfas*const(),
997+
count:usize = count,
998+
size:usize = size_of::<T>(),
999+
) => runtime_add_nowrap(this, count, size)
1000+
);
1001+
9431002
// SAFETY: the caller must uphold the safety contract for `offset`.
9441003
unsafe{ intrinsics::offset(self, count)}
9451004
}
@@ -1018,14 +1077,41 @@ impl<T: ?Sized> *mut T {
10181077
where
10191078
T:Sized,
10201079
{
1080+
#[inline]
1081+
constfnruntime_sub_nowrap(this:*const(),count:usize,size:usize) -> bool{
1082+
#[inline]
1083+
fnruntime(this:*const(),count:usize,size:usize) -> bool{
1084+
letSome(byte_offset) = count.checked_mul(size)else{
1085+
returnfalse;
1086+
};
1087+
byte_offset <= (isize::MAXasusize) && this.addr() >= byte_offset
1088+
}
1089+
1090+
constfncomptime(_:*const(), _:usize, _:usize) -> bool{
1091+
true
1092+
}
1093+
1094+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1095+
}
1096+
1097+
ub_checks::assert_unsafe_precondition!(
1098+
check_language_ub,
1099+
"ptr::sub requires that the address calculation does not overflow",
1100+
(
1101+
this:*const() = selfas*const(),
1102+
count:usize = count,
1103+
size:usize = size_of::<T>(),
1104+
) => runtime_sub_nowrap(this, count, size)
1105+
);
1106+
10211107
ifT::IS_ZST{
10221108
// Pointer arithmetic does nothing when the pointee is a ZST.
10231109
self
10241110
}else{
10251111
// SAFETY: the caller must uphold the safety contract for `offset`.
10261112
// Because the pointee is *not* a ZST, that means that `count` is
10271113
// at most `isize::MAX`, and thus the negation cannot overflow.
1028-
unsafe{self.offset((count asisize).unchecked_neg())}
1114+
unsafe{intrinsics::offset(self, intrinsics::unchecked_sub(0,count asisize))}
10291115
}
10301116
}
10311117

‎tests/codegen/option-as-slice.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ pub fn u64_opt_as_slice(o: &Option<u64>) -> &[u64] {
1414
// CHECK-NOT: br
1515
// CHECK-NOT: switch
1616
// CHECK-NOT: icmp
17-
// CHECK: %[[LEN:.+]] = load i64,{{.+}} !range ![[META_U64:.+]], !noundef
17+
// CHECK: %[[LEN:.+]] = load i64
18+
// CHECK-SAME: !range ![[META_U64:[0-9]+]],
19+
// CHECK-SAME: !noundef
1820
// CHECK-NOT: select
1921
// CHECK-NOT: br
2022
// CHECK-NOT: switch
@@ -51,7 +53,9 @@ pub fn u8_opt_as_slice(o: &Option<u8>) -> &[u8] {
5153
// CHECK-NOT: br
5254
// CHECK-NOT: switch
5355
// CHECK-NOT: icmp
54-
// CHECK: %[[TAG:.+]] = load i8,{{.+}} !range ![[META_U8:.+]], !noundef
56+
// CHECK: %[[TAG:.+]] = load i8
57+
// CHECK-SAME: !range ![[META_U8:[0-9]+]],
58+
// CHECK-SAME: !noundef
5559
// CHECK: %[[LEN:.+]] = zext{{.*}} i8 %[[TAG]] to i64
5660
// CHECK-NOT: select
5761
// CHECK-NOT: br

‎tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-abort.mir‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
1010
scope 2 (inlined std::ptr::const_ptr::<impl *const [u32]>::cast::<u8>) {
1111
}
1212
scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
13+
scope 4 (inlined core::ub_checks::check_language_ub) {
14+
scope 5 (inlined core::ub_checks::check_language_ub::runtime) {
15+
}
16+
}
17+
scope 6 (inlined std::mem::size_of::<u8>) {
18+
}
1319
}
14-
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
20+
scope 7 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
1521
let mut _5: usize;
16-
scope 5 (inlined std::ptr::metadata::<[u32]>) {
22+
scope 8 (inlined std::ptr::metadata::<[u32]>) {
1723
}
18-
scope 6 (inlined std::ptr::from_raw_parts::<[u32], ()>) {
24+
scope 9 (inlined std::ptr::from_raw_parts::<[u32], ()>) {
1925
}
2026
}
2127
}

‎tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_fat.PreCodegen.after.panic-unwind.mir‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,18 @@ fn demo_byte_add_fat(_1: *const [u32], _2: usize) -> *const [u32] {
1010
scope 2 (inlined std::ptr::const_ptr::<impl *const [u32]>::cast::<u8>) {
1111
}
1212
scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
13+
scope 4 (inlined core::ub_checks::check_language_ub) {
14+
scope 5 (inlined core::ub_checks::check_language_ub::runtime) {
15+
}
16+
}
17+
scope 6 (inlined std::mem::size_of::<u8>) {
18+
}
1319
}
14-
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
20+
scope 7 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<[u32]>) {
1521
let mut _5: usize;
16-
scope 5 (inlined std::ptr::metadata::<[u32]>) {
22+
scope 8 (inlined std::ptr::metadata::<[u32]>) {
1723
}
18-
scope 6 (inlined std::ptr::from_raw_parts::<[u32], ()>) {
24+
scope 9 (inlined std::ptr::from_raw_parts::<[u32], ()>) {
1925
}
2026
}
2127
}

‎tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-abort.mir‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 {
1010
scope 2 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<u8>) {
1111
}
1212
scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
13+
scope 4 (inlined core::ub_checks::check_language_ub) {
14+
scope 5 (inlined core::ub_checks::check_language_ub::runtime) {
15+
}
16+
}
17+
scope 6 (inlined std::mem::size_of::<u8>) {
18+
}
1319
}
14-
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<u32>) {
15-
scope 5 (inlined std::ptr::metadata::<u32>) {
20+
scope 7 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<u32>) {
21+
scope 8 (inlined std::ptr::metadata::<u32>) {
1622
}
17-
scope 6 (inlined std::ptr::from_raw_parts::<u32, ()>) {
23+
scope 9 (inlined std::ptr::from_raw_parts::<u32, ()>) {
1824
}
1925
}
2026
}

‎tests/mir-opt/pre-codegen/ptr_offset.demo_byte_add_thin.PreCodegen.after.panic-unwind.mir‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ fn demo_byte_add_thin(_1: *const u32, _2: usize) -> *const u32 {
1010
scope 2 (inlined std::ptr::const_ptr::<impl *const u32>::cast::<u8>) {
1111
}
1212
scope 3 (inlined std::ptr::const_ptr::<impl *const u8>::add) {
13+
scope 4 (inlined core::ub_checks::check_language_ub) {
14+
scope 5 (inlined core::ub_checks::check_language_ub::runtime) {
15+
}
16+
}
17+
scope 6 (inlined std::mem::size_of::<u8>) {
18+
}
1319
}
14-
scope 4 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<u32>) {
15-
scope 5 (inlined std::ptr::metadata::<u32>) {
20+
scope 7 (inlined std::ptr::const_ptr::<impl *const u8>::with_metadata_of::<u32>) {
21+
scope 8 (inlined std::ptr::metadata::<u32>) {
1622
}
17-
scope 6 (inlined std::ptr::from_raw_parts::<u32, ()>) {
23+
scope 9 (inlined std::ptr::from_raw_parts::<u32, ()>) {
1824
}
1925
}
2026
}

0 commit comments

Comments
 (0)