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 8293e74

Browse files
committedOct 7, 2024
Add precondition checks to ptr::offset, ptr::add, ptr::sub
1 parent c65244c commit 8293e74

File tree

2 files changed

+173
-3
lines changed

2 files changed

+173
-3
lines changed
 

‎core/src/ptr/const_ptr.rs

+86-2
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+
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
400+
#[inline]
401+
fn runtime(this: *const (), count: isize, size: usize) -> bool {
402+
// We know `size <= isize::MAX` so the `as` cast here is not lossy.
403+
let Some(byte_offset) = count.checked_mul(size as isize) else {
404+
return false;
405+
};
406+
let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);
407+
!overflow
408+
}
409+
410+
const fn comptime(_: *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 () = self as *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+
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
892+
#[inline]
893+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
894+
let Some(byte_offset) = count.checked_mul(size) else {
895+
return false;
896+
};
897+
let (_, overflow) = this.addr().overflowing_add(byte_offset);
898+
byte_offset <= (isize::MAX as usize) && !overflow
899+
}
900+
901+
const fn comptime(_: *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 () = self as *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+
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
998+
#[inline]
999+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1000+
let Some(byte_offset) = count.checked_mul(size) else {
1001+
return false;
1002+
};
1003+
byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset
1004+
}
1005+
1006+
const fn comptime(_: *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 () = self as *const (),
1018+
count: usize = count,
1019+
size: usize = size_of::<T>(),
1020+
) => runtime_sub_nowrap(this, count, size)
1021+
);
1022+
9391023
if T::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 as isize).unchecked_neg()) }
1030+
unsafe { intrinsics::offset(self, intrinsics::unchecked_sub(0, count as isize)) }
9471031
}
9481032
}
9491033

‎core/src/ptr/mut_ptr.rs

+87-1
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+
const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {
398+
#[inline]
399+
fn runtime(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+
let Some(byte_offset) = count.checked_mul(size as isize) else {
403+
return false;
404+
};
405+
let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);
406+
!overflow
407+
}
408+
409+
const fn comptime(_: *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 () = self as *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+
const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {
976+
#[inline]
977+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
978+
let Some(byte_offset) = count.checked_mul(size) else {
979+
return false;
980+
};
981+
let (_, overflow) = this.addr().overflowing_add(byte_offset);
982+
byte_offset <= (isize::MAX as usize) && !overflow
983+
}
984+
985+
const fn comptime(_: *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 () = self as *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+
const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {
1082+
#[inline]
1083+
fn runtime(this: *const (), count: usize, size: usize) -> bool {
1084+
let Some(byte_offset) = count.checked_mul(size) else {
1085+
return false;
1086+
};
1087+
byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset
1088+
}
1089+
1090+
const fn comptime(_: *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 () = self as *const (),
1102+
count: usize = count,
1103+
size: usize = size_of::<T>(),
1104+
) => runtime_sub_nowrap(this, count, size)
1105+
);
1106+
10211107
if T::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 as isize).unchecked_neg()) }
1114+
unsafe { intrinsics::offset(self, intrinsics::unchecked_sub(0, count as isize)) }
10291115
}
10301116
}
10311117

0 commit comments

Comments
 (0)
Failed to load comments.