Skip to content

Commit 922c51a

Browse files
Contracts & Harnesses for Reference Conversion APIs at std::NonNull (rust-lang#116)
Description: This PR introduces function contracts and proof harness for the NonNull pointer in the Rust core library. Specifically, it verifies reference conversion APIs, covering: - NonNull<T>::as_mut<'a> - NonNull<T>::as_ref<'a> - NonNull<T>::as_uninit_mut<'a> - NonNull<T>::as_uninit_ref<'a> - NonNull<T>::as_uninit_slice<'a> - NonNull<T>::as_uninit_slice_mut<'a> - NonNull<T>::get_unchecked_mut<I> Proof harness: - non_null_check_as_mut - non_null_check_as_ref - non_null_check_as_uninit_mut - non_null_check_as_as_uninit_ref - non_null_check_as_as_uninit_slice - non_null_check_as_uninit_slice_mut - non_null_check_as_get_unchecked_mut Towards model-checking#53 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent 94a31f5 commit 922c51a

File tree

1 file changed

+123
-1
lines changed

1 file changed

+123
-1
lines changed

library/core/src/ptr/non_null.rs

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ impl<T: Sized> NonNull<T> {
139139
#[must_use]
140140
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
141141
#[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")]
142+
#[requires(ub_checks::can_dereference(self.as_ptr()))] // Ensure the pointer is valid to create a reference.
143+
#[ensures(|result: &&MaybeUninit<T>| core::ptr::eq(*result, self.cast().as_ptr()))] // Ensure returned reference points to the correct memory location.
142144
pub const unsafe fn as_uninit_ref<'a>(self) -> &'a MaybeUninit<T> {
143145
// SAFETY: the caller must guarantee that `self` meets all the
144146
// requirements for a reference.
@@ -163,6 +165,8 @@ impl<T: Sized> NonNull<T> {
163165
#[must_use]
164166
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
165167
#[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")]
168+
#[requires(ub_checks::can_dereference(self.as_ptr()))] // Ensure pointer is valid to create a mutable reference.
169+
#[ensures(|result: &&mut MaybeUninit<T>| core::ptr::eq(*result, self.cast().as_ptr()))] // Ensure the returned reference points to the correct memory.
166170
pub const unsafe fn as_uninit_mut<'a>(self) -> &'a mut MaybeUninit<T> {
167171
// SAFETY: the caller must guarantee that `self` meets all the
168172
// requirements for a reference.
@@ -386,6 +390,8 @@ impl<T: ?Sized> NonNull<T> {
386390
#[rustc_const_stable(feature = "const_nonnull_as_ref", since = "1.73.0")]
387391
#[must_use]
388392
#[inline(always)]
393+
#[requires(ub_checks::can_dereference(self.as_ptr() as *const()))] // Ensure input is convertible to a reference
394+
#[ensures(|result: &&T| core::ptr::eq(*result, self.as_ptr()))] // Ensure returned reference matches pointer
389395
pub const unsafe fn as_ref<'a>(&self) -> &'a T {
390396
// SAFETY: the caller must guarantee that `self` meets all the
391397
// requirements for a reference.
@@ -424,6 +430,9 @@ impl<T: ?Sized> NonNull<T> {
424430
#[rustc_const_stable(feature = "const_ptr_as_ref", since = "1.83.0")]
425431
#[must_use]
426432
#[inline(always)]
433+
#[requires(ub_checks::can_dereference(self.as_ptr() as *const()))]
434+
// verify result (a mutable reference) is still associated with the same memory address as the raw pointer stored in self
435+
#[ensures(|result: &&mut T| core::ptr::eq(*result, self.as_ptr()))]
427436
pub const unsafe fn as_mut<'a>(&mut self) -> &'a mut T {
428437
// SAFETY: the caller must guarantee that `self` meets all the
429438
// requirements for a mutable reference.
@@ -1642,6 +1651,11 @@ impl<T> NonNull<[T]> {
16421651
#[must_use]
16431652
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
16441653
#[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")]
1654+
#[requires(self.as_ptr().cast::<T>().align_offset(core::mem::align_of::<T>()) == 0)] // Ensure the pointer is properly aligned
1655+
#[requires(self.len().checked_mul(core::mem::size_of::<T>()).is_some() && self.len() * core::mem::size_of::<T>() <= isize::MAX as usize)] // Ensure the slice size does not exceed isize::MAX
1656+
#[requires(kani::mem::same_allocation(self.as_ptr() as *const(), self.as_ptr().byte_add(self.len() * core::mem::size_of::<T>()) as *const()))] // Ensure the slice is contained within a single allocation
1657+
#[ensures(|result: &&[MaybeUninit<T>]| result.len() == self.len())] // Length check
1658+
#[ensures(|result: &&[MaybeUninit<T>]| core::ptr::eq(result.as_ptr(), self.cast().as_ptr()))] // Ensure the memory addresses match.
16451659
pub const unsafe fn as_uninit_slice<'a>(self) -> &'a [MaybeUninit<T>] {
16461660
// SAFETY: the caller must uphold the safety contract for `as_uninit_slice`.
16471661
unsafe { slice::from_raw_parts(self.cast().as_ptr(), self.len()) }
@@ -1707,6 +1721,11 @@ impl<T> NonNull<[T]> {
17071721
#[must_use]
17081722
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
17091723
#[rustc_const_unstable(feature = "ptr_as_uninit", issue = "75402")]
1724+
#[requires(self.as_ptr().cast::<T>().align_offset(core::mem::align_of::<T>()) == 0)] // Ensure the pointer is properly aligned
1725+
#[requires(self.len().checked_mul(core::mem::size_of::<T>()).is_some() && self.len() * core::mem::size_of::<T>() <= isize::MAX as usize)] // Ensure the slice size does not exceed isize::MAX
1726+
#[requires(kani::mem::same_allocation(self.as_ptr() as *const(), self.as_ptr().byte_add(self.len() * core::mem::size_of::<T>()) as *const()))] // Ensure the slice is contained within a single allocation
1727+
#[ensures(|result: &&mut [MaybeUninit<T>]| result.len() == self.len())] // Length check
1728+
#[ensures(|result: &&mut [MaybeUninit<T>]| core::ptr::eq(result.as_ptr(), self.cast().as_ptr()))] // Address check
17101729
pub const unsafe fn as_uninit_slice_mut<'a>(self) -> &'a mut [MaybeUninit<T>] {
17111730
// SAFETY: the caller must uphold the safety contract for `as_uninit_slice_mut`.
17121731
unsafe { slice::from_raw_parts_mut(self.cast().as_ptr(), self.len()) }
@@ -1735,6 +1754,7 @@ impl<T> NonNull<[T]> {
17351754
/// ```
17361755
#[unstable(feature = "slice_ptr_get", issue = "74265")]
17371756
#[inline]
1757+
#[requires(ub_checks::can_dereference(self.as_ptr()))] // Ensure self can be dereferenced
17381758
pub unsafe fn get_unchecked_mut<I>(self, index: I) -> NonNull<I::Output>
17391759
where
17401760
I: SliceIndex<[T]>,
@@ -1871,7 +1891,7 @@ mod verify {
18711891
let maybe_null_ptr = if kani::any() { xptr as *mut i32 } else { null_mut() };
18721892
let _ = NonNull::new(maybe_null_ptr);
18731893
}
1874-
1894+
18751895
// pub const unsafe fn read(self) -> T where T: Sized
18761896
#[kani::proof_for_contract(NonNull::read)]
18771897
pub fn non_null_check_read() {
@@ -1996,4 +2016,106 @@ mod verify {
19962016
// Trigger panic
19972017
let offset = nonnull_xptr.align_offset(invalid_align);
19982018
}
2019+
2020+
#[kani::proof_for_contract(NonNull::as_mut)]
2021+
pub fn non_null_check_as_mut() {
2022+
let mut x: i32 = kani::any();
2023+
if let Some(mut ptr) = NonNull::new(&mut x as *mut i32) {
2024+
unsafe {
2025+
let result = ptr.as_mut();
2026+
}
2027+
}
2028+
}
2029+
2030+
#[kani::proof_for_contract(NonNull::as_ref)]
2031+
pub fn non_null_check_as_ref() {
2032+
let mut x: i32 = kani::any();
2033+
if let Some(ptr) = NonNull::new(&mut x as *mut i32) {
2034+
unsafe {
2035+
let _ = ptr.as_ref();
2036+
}
2037+
}
2038+
}
2039+
2040+
#[kani::proof_for_contract(NonNull::as_uninit_mut)]
2041+
pub fn non_null_check_as_uninit_mut() {
2042+
use core::mem::MaybeUninit;
2043+
2044+
// Create an uninitialized MaybeUninit value
2045+
let mut uninit: MaybeUninit<i32> = MaybeUninit::uninit();
2046+
let mut ptr = NonNull::new(uninit.as_mut_ptr()).unwrap();
2047+
2048+
unsafe {
2049+
let _ = ptr.as_uninit_mut();
2050+
}
2051+
}
2052+
2053+
#[kani::proof_for_contract(NonNull::as_uninit_ref)]
2054+
pub fn non_null_check_as_uninit_ref() {
2055+
use core::mem::MaybeUninit;
2056+
2057+
// Create an uninitialized MaybeUninit value
2058+
let mut uninit: MaybeUninit<i32> = MaybeUninit::uninit();
2059+
let ptr = NonNull::new(uninit.as_mut_ptr()).unwrap();
2060+
2061+
unsafe {
2062+
let uninit_ref = ptr.as_uninit_ref();
2063+
}
2064+
}
2065+
2066+
#[kani::proof_for_contract(NonNull::as_uninit_slice)]
2067+
pub fn non_null_check_as_uninit_slice() {
2068+
use core::mem::MaybeUninit;
2069+
2070+
const SIZE: usize = 100000;
2071+
let arr: [MaybeUninit<i32>; SIZE] = MaybeUninit::uninit_array();
2072+
let slice: &[MaybeUninit<i32>] = kani::slice::any_slice_of_array(&arr);
2073+
let ptr = NonNull::slice_from_raw_parts(
2074+
NonNull::new(slice.as_ptr() as *mut MaybeUninit<i32>).unwrap(),
2075+
slice.len(),
2076+
);
2077+
2078+
unsafe {
2079+
let _ = ptr.as_uninit_slice();
2080+
}
2081+
}
2082+
2083+
#[kani::proof_for_contract(NonNull::as_uninit_slice_mut)]
2084+
pub fn non_null_check_as_uninit_slice_mut() {
2085+
use core::mem::MaybeUninit;
2086+
2087+
const SIZE: usize = 100000;
2088+
let mut arr: [MaybeUninit<i32>; SIZE] = MaybeUninit::uninit_array();
2089+
let slice: &[MaybeUninit<i32>] = kani::slice::any_slice_of_array(&mut arr);
2090+
let ptr = NonNull::slice_from_raw_parts(
2091+
NonNull::new(slice.as_ptr() as *mut MaybeUninit<i32>).unwrap(),
2092+
SIZE,
2093+
);
2094+
2095+
unsafe {
2096+
let _ = ptr.as_uninit_slice_mut();
2097+
}
2098+
}
2099+
2100+
#[kani::proof_for_contract(NonNull::get_unchecked_mut)]
2101+
pub fn non_null_check_get_unchecked_mut() {
2102+
const ARR_SIZE: usize = 100000;
2103+
let mut arr: [i32; ARR_SIZE] = kani::any();
2104+
let raw_ptr = arr.as_mut_ptr();
2105+
let ptr = NonNull::slice_from_raw_parts(
2106+
NonNull::new(raw_ptr).unwrap(),
2107+
ARR_SIZE,
2108+
);
2109+
let lower = kani::any_where(|x| *x < ARR_SIZE);
2110+
let upper = kani::any_where(|x| *x < ARR_SIZE && *x >= lower);
2111+
unsafe {
2112+
// NOTE: The `index` parameter cannot be used in the function contracts without being moved.
2113+
// Since the `SliceIndex` trait does not guarantee that `index` implements `Clone` or `Copy`,
2114+
// it cannot be reused after being consumed in the precondition. To comply with Rust's ownership
2115+
// rules and ensure `index` is only used once, the in-bounds check is moved to the proof harness
2116+
// as a workaround.
2117+
kani::assume(ptr.as_ref().get(lower..upper).is_some());
2118+
let _ = ptr.get_unchecked_mut(lower..upper);
2119+
}
2120+
}
19992121
}

0 commit comments

Comments
 (0)