Skip to content

Commit 557d607

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

File tree

2 files changed

+170
-1
lines changed

2 files changed

+170
-1
lines changed

library/core/src/ptr/const_ptr.rs

+85-1
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,37 @@ impl<T: ?Sized> *const T {
394394
where
395395
T: Sized,
396396
{
397+
#[inline]
398+
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
399+
#[inline]
400+
fn runtime(this: *const (), count: isize, size: usize) -> bool {
401+
let Some(byte_offset) = count.checked_mul(size as isize) else {
402+
return false;
403+
};
404+
if byte_offset < 0 {
405+
-byte_offset as usize <= this.addr()
406+
} else {
407+
this.addr().checked_add(byte_offset as usize).is_some()
408+
}
409+
}
410+
411+
const fn comptime(_: *const (), _: isize, _: usize) -> bool {
412+
true
413+
}
414+
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 () = self as *const (),
423+
count: isize = count,
424+
size: usize = size_of::<T>(),
425+
) => runtime_offset_nowrap(this, count, size)
426+
);
427+
397428
// SAFETY: the caller must uphold the safety contract for `offset`.
398429
unsafe { intrinsics::offset(self, count) }
399430
}
@@ -728,7 +759,6 @@ impl<T: ?Sized> *const T {
728759
true
729760
}
730761

731-
#[allow(unused_unsafe)]
732762
intrinsics::const_eval_select((this, origin), comptime, runtime)
733763
}
734764

@@ -855,6 +885,33 @@ impl<T: ?Sized> *const T {
855885
where
856886
T: Sized,
857887
{
888+
#[inline]
889+
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
890+
#[inline]
891+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
892+
let Some(byte_offset) = count.checked_mul(size) else {
893+
return false;
894+
};
895+
this.addr().checked_add(byte_offset as usize).is_some()
896+
}
897+
898+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
899+
true
900+
}
901+
902+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
903+
}
904+
905+
ub_checks::assert_unsafe_precondition!(
906+
check_language_ub,
907+
"ptr::add requires that the address calculation does not overflow",
908+
(
909+
this: *const () = self as *const (),
910+
count: usize = count,
911+
size: usize = size_of::<T>(),
912+
) => runtime_add_nowrap(this, count, size)
913+
);
914+
858915
// SAFETY: the caller must uphold the safety contract for `offset`.
859916
unsafe { intrinsics::offset(self, count) }
860917
}
@@ -930,6 +987,33 @@ impl<T: ?Sized> *const T {
930987
where
931988
T: Sized,
932989
{
990+
#[inline]
991+
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
992+
#[inline]
993+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
994+
let Some(byte_offset) = count.checked_mul(size) else {
995+
return false;
996+
};
997+
this.addr() >= byte_offset
998+
}
999+
1000+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
1001+
true
1002+
}
1003+
1004+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1005+
}
1006+
1007+
ub_checks::assert_unsafe_precondition!(
1008+
check_language_ub,
1009+
"ptr::sub requires that the address calculation does not overflow",
1010+
(
1011+
this: *const () = self as *const (),
1012+
count: usize = count,
1013+
size: usize = size_of::<T>(),
1014+
) => runtime_sub_nowrap(this, count, size)
1015+
);
1016+
9331017
if T::IS_ZST {
9341018
// Pointer arithmetic does nothing when the pointee is a ZST.
9351019
self

library/core/src/ptr/mut_ptr.rs

+85
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,37 @@ impl<T: ?Sized> *mut T {
392392
where
393393
T: Sized,
394394
{
395+
#[inline]
396+
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
397+
#[inline]
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+
395426
// SAFETY: the caller must uphold the safety contract for `offset`.
396427
// The obtained pointer is valid for writes since the caller must
397428
// guarantee that it points to the same allocated object as `self`.
@@ -936,6 +967,33 @@ impl<T: ?Sized> *mut T {
936967
where
937968
T: Sized,
938969
{
970+
#[inline]
971+
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
972+
#[inline]
973+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
974+
let Some(byte_offset) = count.checked_mul(size) else {
975+
return false;
976+
};
977+
this.addr().checked_add(byte_offset as usize).is_some()
978+
}
979+
980+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
981+
true
982+
}
983+
984+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
985+
}
986+
987+
ub_checks::assert_unsafe_precondition!(
988+
check_language_ub,
989+
"ptr::add requires that the address calculation does not overflow",
990+
(
991+
this: *const () = self as *const (),
992+
count: usize = count,
993+
size: usize = size_of::<T>(),
994+
) => runtime_add_nowrap(this, count, size)
995+
);
996+
939997
// SAFETY: the caller must uphold the safety contract for `offset`.
940998
unsafe { intrinsics::offset(self, count) }
941999
}
@@ -1011,6 +1069,33 @@ impl<T: ?Sized> *mut T {
10111069
where
10121070
T: Sized,
10131071
{
1072+
#[inline]
1073+
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
1074+
#[inline]
1075+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1076+
let Some(byte_offset) = count.checked_mul(size) else {
1077+
return false;
1078+
};
1079+
this.addr() >= byte_offset
1080+
}
1081+
1082+
const fn comptime(_: *const (), _: usize, _: usize) -> bool {
1083+
true
1084+
}
1085+
1086+
intrinsics::const_eval_select((this, count, size), comptime, runtime)
1087+
}
1088+
1089+
ub_checks::assert_unsafe_precondition!(
1090+
check_language_ub,
1091+
"ptr::sub requires that the address calculation does not overflow",
1092+
(
1093+
this: *const () = self as *const (),
1094+
count: usize = count,
1095+
size: usize = size_of::<T>(),
1096+
) => runtime_sub_nowrap(this, count, size)
1097+
);
1098+
10141099
if T::IS_ZST {
10151100
// Pointer arithmetic does nothing when the pointee is a ZST.
10161101
self

0 commit comments

Comments
 (0)