Skip to content

Commit 7415c26

Browse files
committed
Apply the changes to intrinsics module file
1 parent 1926ee3 commit 7415c26

File tree

1 file changed

+134
-15
lines changed
  • library/core/src/intrinsics

1 file changed

+134
-15
lines changed

library/core/src/intrinsics/mod.rs

+134-15
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@
6464
)]
6565
#![allow(missing_docs)]
6666

67-
use safety::requires;
6867
use crate::marker::{DiscriminantKind, Tuple};
6968
use crate::mem::SizedTypeProperties;
7069
use crate::{ptr, ub_checks};
70+
use safety::{ensures, requires};
7171

7272
#[cfg(kani)]
7373
use crate::kani;
@@ -3663,7 +3663,8 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
36633663
#[requires(ub_checks::can_dereference(x) && ub_checks::can_write(x))]
36643664
#[requires(ub_checks::can_dereference(y) && ub_checks::can_write(y))]
36653665
#[requires(x.addr() != y.addr() || core::mem::size_of::<T>() == 0)]
3666-
#[requires((x.addr() >= y.addr() + core::mem::size_of::<T>()) || (y.addr() >= x.addr() + core::mem::size_of::<T>()))]
3666+
#[requires(ub_checks::maybe_is_nonoverlapping(x as *const (), y as *const (), size_of::<T>(), 1))]
3667+
#[ensures(|_| ub_checks::can_dereference(x) && ub_checks::can_dereference(y))]
36673668
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
36683669
// SAFETY: The caller provided single non-overlapping items behind
36693670
// pointers, so swapping them with `count: 1` is fine.
@@ -3737,6 +3738,9 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
37373738
#[unstable(feature = "core_intrinsics", issue = "none")]
37383739
#[rustc_intrinsic]
37393740
#[rustc_intrinsic_must_be_overridden]
3741+
// VTable pointers must be valid for dereferencing at least 3 `usize` (size, alignment and drop):
3742+
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/166>
3743+
#[requires(ub_checks::can_dereference(_ptr as *const [usize; 3]))]
37403744
pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
37413745
unreachable!()
37423746
}
@@ -3750,6 +3754,9 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize {
37503754
#[unstable(feature = "core_intrinsics", issue = "none")]
37513755
#[rustc_intrinsic]
37523756
#[rustc_intrinsic_must_be_overridden]
3757+
// VTable pointers must be valid for dereferencing at least 3 `usize` (size, alignment and drop):
3758+
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/166>
3759+
#[requires(ub_checks::can_dereference(_ptr as *const [usize; 3]))]
37533760
pub unsafe fn vtable_align(_ptr: *const ()) -> usize {
37543761
unreachable!()
37553762
}
@@ -4034,6 +4041,13 @@ pub const fn ptr_metadata<P: ptr::Pointee<Metadata = M> + ?Sized, M>(_ptr: *cons
40344041
#[inline(always)]
40354042
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
40364043
#[rustc_diagnostic_item = "ptr_copy_nonoverlapping"]
4044+
// Copy is "untyped".
4045+
#[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
4046+
#[requires(!count.overflowing_mul(size_of::<T>()).1
4047+
&& ub_checks::can_dereference(core::ptr::slice_from_raw_parts(src as *const crate::mem::MaybeUninit<T>, count))
4048+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count))
4049+
&& ub_checks::maybe_is_nonoverlapping(src as *const (), dst as *const (), size_of::<T>(), count))]
4050+
#[ensures(|_| { check_copy_untyped(src, dst, count)})]
40374051
pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize) {
40384052
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0"))]
40394053
#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)]
@@ -4141,6 +4155,11 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
41414155
#[inline(always)]
41424156
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
41434157
#[rustc_diagnostic_item = "ptr_copy"]
4158+
#[requires(!count.overflowing_mul(size_of::<T>()).1
4159+
&& ub_checks::can_dereference(core::ptr::slice_from_raw_parts(src as *const crate::mem::MaybeUninit<T>, count))
4160+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count)))]
4161+
#[ensures(|_| { check_copy_untyped(src, dst, count) })]
4162+
#[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
41444163
pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
41454164
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0"))]
41464165
#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)]
@@ -4225,6 +4244,12 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
42254244
#[inline(always)]
42264245
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
42274246
#[rustc_diagnostic_item = "ptr_write_bytes"]
4247+
#[requires(!count.overflowing_mul(size_of::<T>()).1
4248+
&& ub_checks::can_write(core::ptr::slice_from_raw_parts_mut(dst, count)))]
4249+
#[requires(ub_checks::maybe_is_aligned_and_not_null(dst as *const (), align_of::<T>(), T::IS_ZST || count == 0))]
4250+
#[ensures(|_|
4251+
ub_checks::can_dereference(crate::ptr::slice_from_raw_parts(dst as *const u8, count * size_of::<T>())))]
4252+
#[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))]
42284253
pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) {
42294254
#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_ptr_write", since = "1.83.0"))]
42304255
#[cfg_attr(not(bootstrap), rustc_intrinsic_const_stable_indirect)]
@@ -4513,6 +4538,36 @@ pub const unsafe fn copysignf128(_x: f128, _y: f128) -> f128 {
45134538
unimplemented!();
45144539
}
45154540

4541+
/// Return whether the initialization state is preserved.
4542+
///
4543+
/// For untyped copy, done via `copy` and `copy_nonoverlapping`, the copies of non-initialized
4544+
/// bytes (such as padding bytes) should result in a non-initialized copy, while copies of
4545+
/// initialized bytes result in initialized bytes.
4546+
///
4547+
/// It is UB to read the uninitialized bytes, so we cannot compare their values only their
4548+
/// initialization state.
4549+
///
4550+
/// This is used for contracts only.
4551+
///
4552+
/// FIXME: Change this once we add support to quantifiers.
4553+
#[allow(dead_code)]
4554+
#[allow(unused_variables)]
4555+
fn check_copy_untyped<T>(src: *const T, dst: *mut T, count: usize) -> bool {
4556+
#[cfg(kani)]
4557+
if count > 0 {
4558+
let byte = kani::any_where(|sz: &usize| *sz < size_of::<T>());
4559+
let elem = kani::any_where(|val: &usize| *val < count);
4560+
let src_data = src as *const u8;
4561+
let dst_data = unsafe { dst.add(elem) } as *const u8;
4562+
ub_checks::can_dereference(unsafe { src_data.add(byte) })
4563+
== ub_checks::can_dereference(unsafe { dst_data.add(byte) })
4564+
} else {
4565+
true
4566+
}
4567+
#[cfg(not(kani))]
4568+
false
4569+
}
4570+
45164571
/// Inform Miri that a given pointer definitely has a certain alignment.
45174572
#[cfg(miri)]
45184573
#[rustc_allow_const_fn_unstable(const_eval_select)]
@@ -4538,35 +4593,99 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize
45384593
}
45394594

45404595
#[cfg(kani)]
4541-
#[unstable(feature="kani", issue="none")]
4596+
#[unstable(feature = "kani", issue = "none")]
45424597
mod verify {
4543-
use core::{cmp, fmt};
45444598
use super::*;
45454599
use crate::kani;
4600+
use core::mem::MaybeUninit;
4601+
use kani::{AllocationStatus, Arbitrary, ArbitraryPointer, PointerGenerator};
45464602

45474603
#[kani::proof_for_contract(typed_swap)]
45484604
pub fn check_typed_swap_u8() {
4549-
check_swap::<u8>()
4605+
run_with_arbitrary_ptrs::<u8>(|x, y| unsafe { typed_swap(x, y) });
45504606
}
45514607

45524608
#[kani::proof_for_contract(typed_swap)]
45534609
pub fn check_typed_swap_char() {
4554-
check_swap::<char>()
4610+
run_with_arbitrary_ptrs::<char>(|x, y| unsafe { typed_swap(x, y) });
45554611
}
45564612

45574613
#[kani::proof_for_contract(typed_swap)]
45584614
pub fn check_typed_swap_non_zero() {
4559-
check_swap::<core::num::NonZeroI32>()
4615+
run_with_arbitrary_ptrs::<core::num::NonZeroI32>(|x, y| unsafe { typed_swap(x, y) });
45604616
}
45614617

4562-
pub fn check_swap<T: kani::Arbitrary + Copy + cmp::PartialEq + fmt::Debug>() {
4563-
let mut x = kani::any::<T>();
4564-
let old_x = x;
4565-
let mut y = kani::any::<T>();
4566-
let old_y = y;
4618+
#[kani::proof_for_contract(copy)]
4619+
fn check_copy() {
4620+
run_with_arbitrary_ptrs::<char>(|src, dst| unsafe { copy(src, dst, kani::any()) });
4621+
}
45674622

4568-
unsafe { typed_swap(&mut x, &mut y) };
4569-
assert_eq!(y, old_x);
4570-
assert_eq!(x, old_y);
4623+
#[kani::proof_for_contract(copy_nonoverlapping)]
4624+
fn check_copy_nonoverlapping() {
4625+
// Note: cannot use `ArbitraryPointer` here.
4626+
// The `ArbitraryPtr` will arbitrarily initialize memory by indirectly invoking
4627+
// `copy_nonoverlapping`.
4628+
// Kani contract checking would fail due to existing restriction on calls to
4629+
// the function under verification.
4630+
let gen_any_ptr = |buf: &mut [MaybeUninit<char>; 100]| -> *mut char {
4631+
let base = buf.as_mut_ptr() as *mut u8;
4632+
base.wrapping_add(kani::any_where(|offset: &usize| *offset < 400)) as *mut char
4633+
};
4634+
let mut buffer1 = [MaybeUninit::<char>::uninit(); 100];
4635+
for i in 0..100 {
4636+
if kani::any() {
4637+
buffer1[i] = MaybeUninit::new(kani::any());
4638+
}
4639+
}
4640+
let mut buffer2 = [MaybeUninit::<char>::uninit(); 100];
4641+
let src = gen_any_ptr(&mut buffer1);
4642+
let dst = if kani::any() { gen_any_ptr(&mut buffer2) } else { gen_any_ptr(&mut buffer1) };
4643+
unsafe { copy_nonoverlapping(src, dst, kani::any()) }
4644+
}
4645+
4646+
// FIXME: Enable this harness once <https://github.com/model-checking/kani/issues/90> is fixed.
4647+
// Harness triggers a spurious failure when writing 0 bytes to an invalid memory location,
4648+
// which is a safe operation.
4649+
#[cfg(not(kani))]
4650+
#[kani::proof_for_contract(write_bytes)]
4651+
fn check_write_bytes() {
4652+
let mut generator = PointerGenerator::<100>::new();
4653+
let ArbitraryPointer {
4654+
ptr,
4655+
status,
4656+
..
4657+
} = generator.any_alloc_status::<char>();
4658+
kani::assume(supported_status(status));
4659+
unsafe { write_bytes(ptr, kani::any(), kani::any()) };
4660+
}
4661+
4662+
fn run_with_arbitrary_ptrs<T: Arbitrary>(harness: impl Fn(*mut T, *mut T)) {
4663+
let mut generator1 = PointerGenerator::<100>::new();
4664+
let mut generator2 = PointerGenerator::<100>::new();
4665+
let ArbitraryPointer {
4666+
ptr: src,
4667+
status: src_status,
4668+
..
4669+
} = generator1.any_alloc_status::<T>();
4670+
let ArbitraryPointer {
4671+
ptr: dst,
4672+
status: dst_status,
4673+
..
4674+
} = if kani::any() {
4675+
generator1.any_alloc_status::<T>()
4676+
} else {
4677+
generator2.any_alloc_status::<T>()
4678+
};
4679+
kani::assume(supported_status(src_status));
4680+
kani::assume(supported_status(dst_status));
4681+
harness(src, dst);
4682+
}
4683+
4684+
/// Return whether the current status is supported by Kani's contract.
4685+
///
4686+
/// Kani memory predicates currently doesn't support pointers to dangling or dead allocations.
4687+
/// Thus, we have to explicitly exclude those cases.
4688+
fn supported_status(status: AllocationStatus) -> bool {
4689+
status != AllocationStatus::Dangling && status != AllocationStatus::DeadObject
45714690
}
45724691
}

0 commit comments

Comments
 (0)