Skip to content

Commit 9083ec2

Browse files
Dhvani-Kapadiacarolynzechzhassan-awstautschnig
authored
Contract & Harnesses for byte_sub, offset, map_addr and with_addr (#107)
### **Description** This PR includes contracts and proof harnesses for the four APIs, `offset` ,` byte_sub`, `map_addr`, and `with_addr` which are part of the NonNull library in Rust. ### **Changes Overview:** Covered APIs: NonNull::offset: Adds an offset to a pointer NonNull::byte_sub: Calculates an offset from a pointer in bytes. NonNull:: map_addr: Creates a new pointer by mapping self's address to a new one NonNull::with_addr: Creates a new pointer with the given address Proof harness: non_null_check_offset non_null_check_byte_sub non_null_check_map_addr non_null_check_with_addr ### **Revalidation** To revalidate the verification results, run kani verify-std -Z unstable-options "path/to/library" -Z function-contracts -Z mem-predicates --harness ptr::non_null::verify. This will run all four harnesses in the module. All default checks should pass: ``` VERIFICATION:- SUCCESSFUL Verification Time: 0.57787573s Complete - 6 successfully verified harnesses, 0 failures, 6 total. ``` Towards issue #53 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses. --------- Co-authored-by: Carolyn Zech <[email protected]> Co-authored-by: Zyad Hassan <[email protected]> Co-authored-by: Michael Tautschnig <[email protected]>
1 parent 0cc020c commit 9083ec2

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

library/core/src/ptr/non_null.rs

+65-2
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ impl<T: ?Sized> NonNull<T> {
313313
#[must_use]
314314
#[inline]
315315
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
316+
#[ensures(|result: &Self| !result.as_ptr().is_null() && result.addr() == addr)]
316317
pub fn with_addr(self, addr: NonZero<usize>) -> Self {
317318
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
318319
unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
@@ -327,6 +328,7 @@ impl<T: ?Sized> NonNull<T> {
327328
#[must_use]
328329
#[inline]
329330
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
331+
#[ensures(|result: &Self| !result.as_ptr().is_null())]
330332
pub fn map_addr(self, f: impl FnOnce(NonZero<usize>) -> NonZero<usize>) -> Self {
331333
self.with_addr(f(self.addr()))
332334
}
@@ -504,6 +506,12 @@ impl<T: ?Sized> NonNull<T> {
504506
#[must_use = "returns a new pointer rather than modifying its argument"]
505507
#[stable(feature = "non_null_convenience", since = "1.80.0")]
506508
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
509+
#[requires(
510+
count.checked_mul(core::mem::size_of::<T>() as isize).is_some() &&
511+
(self.as_ptr() as isize).checked_add(count.wrapping_mul(core::mem::size_of::<T>() as isize)).is_some() &&
512+
(count == 0 || ub_checks::same_allocation(self.as_ptr() as *const (), self.as_ptr().wrapping_offset(count) as *const ()))
513+
)]
514+
#[ensures(|result: &Self| result.as_ptr() == self.as_ptr().wrapping_offset(count))]
507515
pub const unsafe fn offset(self, count: isize) -> Self
508516
where
509517
T: Sized,
@@ -709,6 +717,14 @@ impl<T: ?Sized> NonNull<T> {
709717
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
710718
#[stable(feature = "non_null_convenience", since = "1.80.0")]
711719
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
720+
#[requires(
721+
count <= (isize::MAX as usize) &&
722+
self.as_ptr().addr().checked_sub(count).is_some() &&
723+
ub_checks::same_allocation(self.as_ptr() as *const (), (self.as_ptr().addr() - count) as *const ())
724+
)]
725+
#[ensures(
726+
|result: &NonNull<T>| result.as_ptr().addr() == (self.as_ptr().addr() - count)
727+
)]
712728
pub const unsafe fn byte_sub(self, count: usize) -> Self {
713729
// SAFETY: the caller must uphold the safety contract for `sub` and `byte_sub` has the same
714730
// safety contract.
@@ -1760,8 +1776,10 @@ impl<T: ?Sized> From<&T> for NonNull<T> {
17601776
#[unstable(feature="kani", issue="none")]
17611777
mod verify {
17621778
use super::*;
1763-
use crate::ptr::null_mut;
17641779
use crate::mem;
1780+
use crate::ptr::null_mut;
1781+
use core::num::NonZeroUsize;
1782+
use core::ptr::NonNull;
17651783
use kani::PointerGenerator;
17661784

17671785
trait SampleTrait {
@@ -2161,7 +2179,6 @@ mod verify {
21612179
struct Droppable {
21622180
value: i32,
21632181
}
2164-
21652182
impl Drop for Droppable {
21662183
fn drop(&mut self) {
21672184
}
@@ -2296,6 +2313,52 @@ mod verify {
22962313
let result = non_null_ptr.is_aligned_to(align);
22972314
}
22982315

2316+
#[kani::proof_for_contract(NonNull::byte_sub)]
2317+
pub fn non_null_check_byte_sub() {
2318+
const SIZE: usize = mem::size_of::<i32>() * 10000;
2319+
let mut generator = PointerGenerator::<SIZE>::new();
2320+
let count: usize = kani::any();
2321+
let raw_ptr: *mut i32 = generator.any_in_bounds().ptr as *mut i32;
2322+
let ptr = NonNull::new(raw_ptr).unwrap();
2323+
unsafe {
2324+
let result = ptr.byte_sub(count);
2325+
}
2326+
}
2327+
2328+
#[kani::proof_for_contract(NonNull::offset)]
2329+
pub fn non_null_check_offset() {
2330+
const SIZE: usize = mem::size_of::<i32>() * 10000;
2331+
let mut generator = PointerGenerator::<SIZE>::new();
2332+
let start_ptr = generator.any_in_bounds().ptr as *mut i32;
2333+
let ptr_nonnull = NonNull::new(start_ptr).unwrap();
2334+
let count: isize = kani::any();
2335+
unsafe {
2336+
let result = ptr_nonnull.offset(count);
2337+
}
2338+
}
2339+
2340+
#[kani::proof_for_contract(NonNull::map_addr)]
2341+
pub fn non_null_check_map_addr() {
2342+
const SIZE: usize = 10000;
2343+
let arr: [i32; SIZE] = kani::any();
2344+
let ptr = NonNull::new(arr.as_ptr() as *mut i32).unwrap();
2345+
let new_offset: usize = kani::any_where(|&x| x <= SIZE);
2346+
let f = |addr: NonZeroUsize| -> NonZeroUsize {
2347+
NonZeroUsize::new(addr.get().wrapping_add(new_offset)).unwrap()
2348+
};
2349+
let result = ptr.map_addr(f);
2350+
}
2351+
2352+
#[kani::proof_for_contract(NonNull::with_addr)]
2353+
pub fn non_null_check_with_addr() {
2354+
const SIZE: usize = 10000;
2355+
let arr: [i32; SIZE] = kani::any();
2356+
let ptr = NonNull::new(arr.as_ptr() as *mut i32).unwrap();
2357+
let new_offset: usize = kani::any_where(|&x| x <= SIZE);
2358+
let new_addr = NonZeroUsize::new(ptr.as_ptr().addr() + new_offset).unwrap();
2359+
let result = ptr.with_addr(new_addr);
2360+
}
2361+
22992362
#[kani::proof_for_contract(NonNull::sub)]
23002363
pub fn non_null_check_sub() {
23012364
const SIZE: usize = 10000;

0 commit comments

Comments
 (0)