Skip to content

Commit 892ee59

Browse files
Contracts & Harnesses for byte_add, byte_offset, and byte_offset_from (rust-lang#103)
# Description: This PR introduces function contracts and proof harness for the NonNull pointer in the Rust core library. Specifically, it verifies three new APIs—`byte_offset`, `byte_add`, and `byte_offset_from` with Kani. These changes enhance the functionality of pointer arithmetic and verification for NonNull pointers. # Changes Overview: **Covered APIs:** 1. `NonNull::byte_add`: Adds a specified byte offset to a pointer. 2. `NonNull::byte_offset`: Allows calculating an offset from a pointer in bytes. 4. `NonNull::byte_offset_from`: Calculates the distance between two pointers in bytes. **Proof harness:** 1. `non_null_byte_add_proof` 2. `non_null_byte_offset_proof` 3. `non_null_byte_offset_from_proof` Towards rust-lang#53
1 parent ca85f7f commit 892ee59

File tree

1 file changed

+86
-0
lines changed

1 file changed

+86
-0
lines changed

Diff for: library/core/src/ptr/non_null.rs

+86
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,11 @@ impl<T: ?Sized> NonNull<T> {
526526
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
527527
#[stable(feature = "non_null_convenience", since = "1.80.0")]
528528
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
529+
#[requires(
530+
(self.as_ptr().addr() as isize).checked_add(count).is_some() &&
531+
kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_offset(count))
532+
)]
533+
#[ensures(|result: &Self| result.as_ptr() == self.as_ptr().wrapping_byte_offset(count))]
529534
pub const unsafe fn byte_offset(self, count: isize) -> Self {
530535
// SAFETY: the caller must uphold the safety contract for `offset` and `byte_offset` has
531536
// the same safety contract.
@@ -607,6 +612,10 @@ impl<T: ?Sized> NonNull<T> {
607612
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
608613
#[stable(feature = "non_null_convenience", since = "1.80.0")]
609614
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
615+
#[requires(
616+
count <= (isize::MAX as usize) - self.as_ptr().addr() && // Ensure the offset doesn't overflow
617+
(count == 0 || kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_add(count))) // Ensure the offset is within the same allocation
618+
)]
610619
pub const unsafe fn byte_add(self, count: usize) -> Self {
611620
// SAFETY: the caller must uphold the safety contract for `add` and `byte_add` has the same
612621
// safety contract.
@@ -820,6 +829,14 @@ impl<T: ?Sized> NonNull<T> {
820829
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
821830
#[stable(feature = "non_null_convenience", since = "1.80.0")]
822831
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
832+
#[requires(
833+
self.as_ptr().addr() == origin.as_ptr().addr() ||
834+
kani::mem::same_allocation(self.as_ptr() as *const(), origin.as_ptr() as *const())
835+
)]
836+
#[ensures(
837+
|result: &isize|
838+
*result == (self.as_ptr() as *const u8).offset_from(origin.as_ptr() as *const u8)
839+
)]
823840
pub const unsafe fn byte_offset_from<U: ?Sized>(self, origin: NonNull<U>) -> isize {
824841
// SAFETY: the caller must uphold the safety contract for `byte_offset_from`.
825842
unsafe { self.pointer.byte_offset_from(origin.pointer) }
@@ -1736,6 +1753,7 @@ impl<T: ?Sized> From<&T> for NonNull<T> {
17361753
mod verify {
17371754
use super::*;
17381755
use crate::ptr::null_mut;
1756+
use crate::mem;
17391757
use kani::PointerGenerator;
17401758

17411759
trait SampleTrait {
@@ -2345,4 +2363,72 @@ mod verify {
23452363
generate_write_volatile_harness!(usize, {core::mem::size_of::<usize>()}, non_null_check_write_volatile_usize);
23462364
generate_write_volatile_harness!((), 1, non_null_check_write_volatile_unit);
23472365

2366+
#[kani::proof_for_contract(NonNull::byte_add)]
2367+
pub fn non_null_byte_add_proof() {
2368+
// Make size as 1000 to ensure the array is large enough to cover various senarios
2369+
// while maintaining a reasonable proof runtime
2370+
const ARR_SIZE: usize = mem::size_of::<i32>() * 1000;
2371+
let mut generator = PointerGenerator::<ARR_SIZE>::new();
2372+
2373+
let count: usize = kani::any();
2374+
let raw_ptr: *mut i32 = generator.any_in_bounds().ptr as *mut i32;
2375+
2376+
unsafe {
2377+
let ptr = NonNull::new(raw_ptr).unwrap();
2378+
let result = ptr.byte_add(count);
2379+
}
2380+
}
2381+
2382+
#[kani::proof_for_contract(NonNull::byte_add)]
2383+
pub fn non_null_byte_add_dangling_proof() {
2384+
let ptr = NonNull::<i32>::dangling();
2385+
unsafe {
2386+
let _ = ptr.byte_add(0);
2387+
}
2388+
}
2389+
2390+
#[kani::proof_for_contract(NonNull::byte_offset)]
2391+
pub fn non_null_byte_offset_proof() {
2392+
const ARR_SIZE: usize = mem::size_of::<i32>() * 1000;
2393+
let mut generator = PointerGenerator::<ARR_SIZE>::new();
2394+
2395+
let count: isize = kani::any();
2396+
let raw_ptr: *mut i32 = generator.any_in_bounds().ptr as *mut i32;
2397+
2398+
unsafe {
2399+
let ptr = NonNull::new(raw_ptr).unwrap();
2400+
let result = ptr.byte_offset(count);
2401+
}
2402+
}
2403+
2404+
#[kani::proof_for_contract(NonNull::byte_offset_from)]
2405+
pub fn non_null_byte_offset_from_proof() {
2406+
const SIZE: usize = mem::size_of::<i32>() * 1000;
2407+
let mut generator1 = PointerGenerator::<SIZE>::new();
2408+
let mut generator2 = PointerGenerator::<SIZE>::new();
2409+
2410+
let ptr: *mut i32 = generator1.any_in_bounds().ptr as *mut i32;
2411+
2412+
let origin: *mut i32 = if kani::any() {
2413+
generator1.any_in_bounds().ptr as *mut i32
2414+
} else {
2415+
generator2.any_in_bounds().ptr as *mut i32
2416+
};
2417+
2418+
let ptr_nonnull = unsafe { NonNull::new(ptr).unwrap() };
2419+
let origin_nonnull = unsafe { NonNull::new(origin).unwrap() };
2420+
2421+
unsafe {
2422+
let result = ptr_nonnull.byte_offset_from(origin_nonnull);
2423+
}
2424+
}
2425+
2426+
#[kani::proof_for_contract(NonNull::byte_offset_from)]
2427+
pub fn non_null_byte_offset_from_dangling_proof() {
2428+
let origin = NonNull::<i32>::dangling();
2429+
let ptr = origin;
2430+
unsafe {
2431+
let _ = ptr.byte_offset_from(origin);
2432+
}
2433+
}
23482434
}

0 commit comments

Comments
 (0)