Skip to content

Commit fe99c6b

Browse files
committed
Add precondition checks to ptr::offset, ptr::add, ptr::sub
1 parent f827364 commit fe99c6b

File tree

2 files changed

+158
-1
lines changed

2 files changed

+158
-1
lines changed

library/core/src/ptr/const_ptr.rs

+79-1
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,35 @@ impl<T: ?Sized> *const T {
394394
where
395395
T: Sized,
396396
{
397+
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
398+
fn runtime(this: *const (), count: isize, size: usize) -> bool {
399+
let Some(byte_offset) = count.checked_mul(size as isize) else {
400+
return false;
401+
};
402+
if byte_offset < 0 {
403+
-byte_offset as usize <= this.addr()
404+
} else {
405+
this.addr().checked_add(byte_offset as usize).is_some()
406+
}
407+
}
408+
409+
const fn comptime(_: *const (), _: isize, _: usize) -> bool {
410+
true
411+
}
412+
413+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
414+
}
415+
416+
ub_checks::assert_unsafe_precondition!(
417+
check_language_ub,
418+
"ptr::offset requires the address calculation to not overflow",
419+
(
420+
this: *const () = self as *const (),
421+
count: isize = count,
422+
size: usize = size_of::<T>(),
423+
) => runtime_offset_nowrap(this, count, size)
424+
);
425+
397426
// SAFETY: the caller must uphold the safety contract for `offset`.
398427
unsafe { intrinsics::offset(self, count) }
399428
}
@@ -728,7 +757,6 @@ impl<T: ?Sized> *const T {
728757
true
729758
}
730759

731-
#[allow(unused_unsafe)]
732760
intrinsics::const_eval_select((this, origin), comptime, runtime)
733761
}
734762

@@ -855,6 +883,31 @@ impl<T: ?Sized> *const T {
855883
where
856884
T: Sized,
857885
{
886+
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
887+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
888+
let Some(byte_offset) = count.checked_mul(size) else {
889+
return false;
890+
};
891+
this.addr().checked_add(byte_offset as usize).is_some()
892+
}
893+
894+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
895+
true
896+
}
897+
898+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
899+
}
900+
901+
ub_checks::assert_unsafe_precondition!(
902+
check_language_ub,
903+
"ptr::add requires that the address calculation does not overflow",
904+
(
905+
this: *const () = self as *const (),
906+
count: usize = count,
907+
size: usize = size_of::<T>(),
908+
) => runtime_add_nowrap(this, count, size)
909+
);
910+
858911
// SAFETY: the caller must uphold the safety contract for `offset`.
859912
unsafe { intrinsics::offset(self, count) }
860913
}
@@ -930,6 +983,31 @@ impl<T: ?Sized> *const T {
930983
where
931984
T: Sized,
932985
{
986+
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
987+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
988+
let Some(byte_offset) = count.checked_mul(size) else {
989+
return false;
990+
};
991+
this.addr() >= byte_offset
992+
}
993+
994+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
995+
true
996+
}
997+
998+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
999+
}
1000+
1001+
ub_checks::assert_unsafe_precondition!(
1002+
check_language_ub,
1003+
"ptr::sub requires that the address calculation does not overflow",
1004+
(
1005+
this: *const () = self as *const (),
1006+
count: usize = count,
1007+
size: usize = size_of::<T>(),
1008+
) => runtime_sub_nowrap(this, count, size)
1009+
);
1010+
9331011
if T::IS_ZST {
9341012
// Pointer arithmetic does nothing when the pointee is a ZST.
9351013
self

library/core/src/ptr/mut_ptr.rs

+79
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,35 @@ impl<T: ?Sized> *mut T {
392392
where
393393
T: Sized,
394394
{
395+
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
396+
fn runtime(this: *const (), count: isize, size: usize) -> bool {
397+
let Some(byte_offset) = count.checked_mul(size as isize) else {
398+
return false;
399+
};
400+
if byte_offset < 0 {
401+
-byte_offset as usize <= this.addr()
402+
} else {
403+
this.addr().checked_add(byte_offset as usize).is_some()
404+
}
405+
}
406+
407+
const fn comptime(_: *const (), _: isize, _: usize) -> bool {
408+
true
409+
}
410+
411+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
412+
}
413+
414+
ub_checks::assert_unsafe_precondition!(
415+
check_language_ub,
416+
"ptr::offset requires the address calculation to not overflow",
417+
(
418+
this: *const () = self as *const (),
419+
count: isize = count,
420+
size: usize = size_of::<T>(),
421+
) => runtime_offset_nowrap(this, count, size)
422+
);
423+
395424
// SAFETY: the caller must uphold the safety contract for `offset`.
396425
// The obtained pointer is valid for writes since the caller must
397426
// guarantee that it points to the same allocated object as `self`.
@@ -936,6 +965,31 @@ impl<T: ?Sized> *mut T {
936965
where
937966
T: Sized,
938967
{
968+
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
969+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
970+
let Some(byte_offset) = count.checked_mul(size) else {
971+
return false;
972+
};
973+
this.addr().checked_add(byte_offset as usize).is_some()
974+
}
975+
976+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
977+
true
978+
}
979+
980+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
981+
}
982+
983+
ub_checks::assert_unsafe_precondition!(
984+
check_language_ub,
985+
"ptr::add requires that the address calculation does not overflow",
986+
(
987+
this: *const () = self as *const (),
988+
count: usize = count,
989+
size: usize = size_of::<T>(),
990+
) => runtime_add_nowrap(this, count, size)
991+
);
992+
939993
// SAFETY: the caller must uphold the safety contract for `offset`.
940994
unsafe { intrinsics::offset(self, count) }
941995
}
@@ -1011,6 +1065,31 @@ impl<T: ?Sized> *mut T {
10111065
where
10121066
T: Sized,
10131067
{
1068+
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
1069+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1070+
let Some(byte_offset) = count.checked_mul(size) else {
1071+
return false;
1072+
};
1073+
this.addr() >= byte_offset
1074+
}
1075+
1076+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
1077+
true
1078+
}
1079+
1080+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1081+
}
1082+
1083+
ub_checks::assert_unsafe_precondition!(
1084+
check_language_ub,
1085+
"ptr::sub requires that the address calculation does not overflow",
1086+
(
1087+
this: *const () = self as *const (),
1088+
count: usize = count,
1089+
size: usize = size_of::<T>(),
1090+
) => runtime_sub_nowrap(this, count, size)
1091+
);
1092+
10141093
if T::IS_ZST {
10151094
// Pointer arithmetic does nothing when the pointee is a ZST.
10161095
self

0 commit comments

Comments
 (0)