Skip to content

Commit 8f5512d

Browse files
Contracts & Harnesses for swap, replace, and drop_in_place (#144)
# Description This PR introduces function contracts and proof harness for the NonNull pointer in the Rust core library. Specifically, it verifies three new APIs—`swap`, `replace`, and `drop_in_place` with Kani. These changes enhance the functionality of memory operations for NonNull pointers. # Change Overview Covered APIs: 1. NonNull::swap: Swaps the values at two mutable locations of the same type 2. NonNull::replace: Replaces the pointer's value, returning the old value 3. NonNull::drop_in_place: Executes the destructor (if any) of the pointed-to value Resolves #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 4db111f commit 8f5512d

File tree

1 file changed

+62
-1
lines changed

1 file changed

+62
-1
lines changed

library/core/src/ptr/non_null.rs

+62-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use crate::ub_checks::assert_unsafe_precondition;
1010
use crate::{fmt, hash, intrinsics, ptr};
1111
use safety::{ensures, requires};
1212

13-
1413
#[cfg(kani)]
1514
use crate::kani;
1615
#[cfg(kani)]
@@ -1060,6 +1059,8 @@ impl<T: ?Sized> NonNull<T> {
10601059
/// [`ptr::drop_in_place`]: crate::ptr::drop_in_place()
10611060
#[inline(always)]
10621061
#[stable(feature = "non_null_convenience", since = "1.80.0")]
1062+
#[requires(ub_checks::can_dereference(self.as_ptr() as *const()))] // Ensure self is aligned, initialized, and valid for read
1063+
#[requires(ub_checks::can_write(self.as_ptr() as *mut()))] // Ensure self is valid for write
10631064
pub unsafe fn drop_in_place(self) {
10641065
// SAFETY: the caller must uphold the safety contract for `drop_in_place`.
10651066
unsafe { ptr::drop_in_place(self.as_ptr()) }
@@ -1151,6 +1152,9 @@ impl<T: ?Sized> NonNull<T> {
11511152
/// [`ptr::replace`]: crate::ptr::replace()
11521153
#[inline(always)]
11531154
#[stable(feature = "non_null_convenience", since = "1.80.0")]
1155+
#[cfg_attr(kani, kani::modifies(self.as_ptr()))]
1156+
#[requires(ub_checks::can_dereference(self.as_ptr()))] // Ensure self is aligned, initialized, and valid for read
1157+
#[requires(ub_checks::can_write(self.as_ptr()))] // Ensure self is valid for write
11541158
pub unsafe fn replace(self, src: T) -> T
11551159
where
11561160
T: Sized,
@@ -1169,6 +1173,9 @@ impl<T: ?Sized> NonNull<T> {
11691173
#[inline(always)]
11701174
#[stable(feature = "non_null_convenience", since = "1.80.0")]
11711175
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
1176+
#[cfg_attr(kani, kani::modifies(self.as_ptr(), with.as_ptr()))]
1177+
#[requires(ub_checks::can_dereference(self.as_ptr()) && ub_checks::can_write(self.as_ptr()))]
1178+
#[requires(ub_checks::can_dereference(with.as_ptr()) && ub_checks::can_write(with.as_ptr()))]
11721179
pub const unsafe fn swap(self, with: NonNull<T>)
11731180
where
11741181
T: Sized,
@@ -2118,4 +2125,58 @@ mod verify {
21182125
let _ = ptr.get_unchecked_mut(lower..upper);
21192126
}
21202127
}
2128+
2129+
#[kani::proof_for_contract(NonNull::replace)]
2130+
pub fn non_null_check_replace() {
2131+
let mut x: i32 = kani::any();
2132+
let mut y: i32 = kani::any();
2133+
2134+
let origin_ptr = NonNull::new(&mut x as *mut i32).unwrap();
2135+
unsafe {
2136+
let captured_original = ptr::read(origin_ptr.as_ptr());
2137+
let replaced = origin_ptr.replace(y);
2138+
let after_replace = ptr::read(origin_ptr.as_ptr());
2139+
2140+
assert_eq!(captured_original, replaced);
2141+
assert_eq!(after_replace, y)
2142+
}
2143+
}
2144+
2145+
#[kani::proof_for_contract(NonNull::drop_in_place)]
2146+
pub fn non_null_check_drop_in_place() {
2147+
struct Droppable {
2148+
value: i32,
2149+
}
2150+
2151+
impl Drop for Droppable {
2152+
fn drop(&mut self) {
2153+
}
2154+
}
2155+
2156+
let mut droppable = Droppable { value: kani::any() };
2157+
let ptr = NonNull::new(&mut droppable as *mut Droppable).unwrap();
2158+
unsafe {
2159+
ptr.drop_in_place();
2160+
}
2161+
}
2162+
2163+
#[kani::proof_for_contract(NonNull::swap)]
2164+
pub fn non_null_check_swap() {
2165+
let mut a: i32 = kani::any();
2166+
let mut b: i32 = kani::any();
2167+
2168+
let ptr_a = NonNull::new(&mut a as *mut i32).unwrap();
2169+
let ptr_b = NonNull::new(&mut b as *mut i32).unwrap();
2170+
2171+
unsafe {
2172+
let old_a = ptr::read(ptr_a.as_ptr());
2173+
let old_b = ptr::read(ptr_b.as_ptr());
2174+
ptr_a.swap(ptr_b);
2175+
// Verify that the values have been swapped.
2176+
let new_a = ptr::read(ptr_a.as_ptr());
2177+
let new_b = ptr::read(ptr_b.as_ptr());
2178+
assert_eq!(old_a, new_b);
2179+
assert_eq!(old_b, new_a);
2180+
}
2181+
}
21212182
}

0 commit comments

Comments
 (0)