Skip to content

Commit 2dd09a2

Browse files
committed
Auto merge of rust-lang#130251 - saethlin:ptr-offset-preconditions, r=<try>
Add precondition checks to ptr::offset, ptr::add, ptr::sub r? `@ghost`
2 parents 394c406 + f879fcb commit 2dd09a2

File tree

3 files changed

+189
-9
lines changed

3 files changed

+189
-9
lines changed

compiler/rustc_mir_transform/src/cost_checker.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use rustc_middle::bug;
22
use rustc_middle::mir::visit::*;
33
use rustc_middle::mir::*;
44
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
5+
use rustc_span::sym;
56

67
const INSTR_COST: usize = 5;
78
const CALL_PENALTY: usize = 25;
@@ -127,14 +128,24 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
127128
}
128129
}
129130
TerminatorKind::Call { func, unwind, .. } => {
130-
self.penalty += if let Some((def_id, ..)) = func.const_fn_def()
131-
&& self.tcx.intrinsic(def_id).is_some()
132-
{
133-
// Don't give intrinsics the extra penalty for calls
134-
INSTR_COST
135-
} else {
136-
CALL_PENALTY
137-
};
131+
let mut is_intrinsic = false;
132+
let mut is_ubcheck = false;
133+
if let Some((def_id, ..)) = func.const_fn_def() {
134+
if self.tcx.intrinsic(def_id).is_some() {
135+
is_intrinsic = true;
136+
}
137+
if self.tcx.has_attr(def_id, sym::rustc_no_mir_inline) {
138+
is_ubcheck = true;
139+
}
140+
}
141+
if !is_ubcheck {
142+
self.penalty += if is_intrinsic {
143+
// Don't give intrinsics the extra penalty for calls
144+
INSTR_COST
145+
} else {
146+
CALL_PENALTY
147+
};
148+
}
138149
if let UnwindAction::Cleanup(_) = unwind {
139150
self.penalty += LANDINGPAD_PENALTY;
140151
}

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)