Skip to content

Commit

Permalink
Contract & Harnesses for byte_sub, offset, map_addr and with_addr (#107)
Browse files Browse the repository at this point in the history
### **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 <cmzech@amazon.com>
Co-authored-by: Zyad Hassan <zyadh@amazon.com>
Co-authored-by: Michael Tautschnig <tautschn@amazon.com>
  • Loading branch information
4 people authored Dec 11, 2024
1 parent 0cc020c commit 9083ec2
Showing 1 changed file with 65 additions and 2 deletions.
67 changes: 65 additions & 2 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ impl<T: ?Sized> NonNull<T> {
#[must_use]
#[inline]
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
#[ensures(|result: &Self| !result.as_ptr().is_null() && result.addr() == addr)]
pub fn with_addr(self, addr: NonZero<usize>) -> Self {
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
Expand All @@ -327,6 +328,7 @@ impl<T: ?Sized> NonNull<T> {
#[must_use]
#[inline]
#[stable(feature = "strict_provenance", since = "CURRENT_RUSTC_VERSION")]
#[ensures(|result: &Self| !result.as_ptr().is_null())]
pub fn map_addr(self, f: impl FnOnce(NonZero<usize>) -> NonZero<usize>) -> Self {
self.with_addr(f(self.addr()))
}
Expand Down Expand Up @@ -504,6 +506,12 @@ impl<T: ?Sized> NonNull<T> {
#[must_use = "returns a new pointer rather than modifying its argument"]
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
#[requires(
count.checked_mul(core::mem::size_of::<T>() as isize).is_some() &&
(self.as_ptr() as isize).checked_add(count.wrapping_mul(core::mem::size_of::<T>() as isize)).is_some() &&
(count == 0 || ub_checks::same_allocation(self.as_ptr() as *const (), self.as_ptr().wrapping_offset(count) as *const ()))
)]
#[ensures(|result: &Self| result.as_ptr() == self.as_ptr().wrapping_offset(count))]
pub const unsafe fn offset(self, count: isize) -> Self
where
T: Sized,
Expand Down Expand Up @@ -709,6 +717,14 @@ impl<T: ?Sized> NonNull<T> {
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
#[requires(
count <= (isize::MAX as usize) &&
self.as_ptr().addr().checked_sub(count).is_some() &&
ub_checks::same_allocation(self.as_ptr() as *const (), (self.as_ptr().addr() - count) as *const ())
)]
#[ensures(
|result: &NonNull<T>| result.as_ptr().addr() == (self.as_ptr().addr() - count)
)]
pub const unsafe fn byte_sub(self, count: usize) -> Self {
// SAFETY: the caller must uphold the safety contract for `sub` and `byte_sub` has the same
// safety contract.
Expand Down Expand Up @@ -1760,8 +1776,10 @@ impl<T: ?Sized> From<&T> for NonNull<T> {
#[unstable(feature="kani", issue="none")]
mod verify {
use super::*;
use crate::ptr::null_mut;
use crate::mem;
use crate::ptr::null_mut;
use core::num::NonZeroUsize;
use core::ptr::NonNull;
use kani::PointerGenerator;

trait SampleTrait {
Expand Down Expand Up @@ -2161,7 +2179,6 @@ mod verify {
struct Droppable {
value: i32,
}

impl Drop for Droppable {
fn drop(&mut self) {
}
Expand Down Expand Up @@ -2296,6 +2313,52 @@ mod verify {
let result = non_null_ptr.is_aligned_to(align);
}

#[kani::proof_for_contract(NonNull::byte_sub)]
pub fn non_null_check_byte_sub() {
const SIZE: usize = mem::size_of::<i32>() * 10000;
let mut generator = PointerGenerator::<SIZE>::new();
let count: usize = kani::any();
let raw_ptr: *mut i32 = generator.any_in_bounds().ptr as *mut i32;
let ptr = NonNull::new(raw_ptr).unwrap();
unsafe {
let result = ptr.byte_sub(count);
}
}

#[kani::proof_for_contract(NonNull::offset)]
pub fn non_null_check_offset() {
const SIZE: usize = mem::size_of::<i32>() * 10000;
let mut generator = PointerGenerator::<SIZE>::new();
let start_ptr = generator.any_in_bounds().ptr as *mut i32;
let ptr_nonnull = NonNull::new(start_ptr).unwrap();
let count: isize = kani::any();
unsafe {
let result = ptr_nonnull.offset(count);
}
}

#[kani::proof_for_contract(NonNull::map_addr)]
pub fn non_null_check_map_addr() {
const SIZE: usize = 10000;
let arr: [i32; SIZE] = kani::any();
let ptr = NonNull::new(arr.as_ptr() as *mut i32).unwrap();
let new_offset: usize = kani::any_where(|&x| x <= SIZE);
let f = |addr: NonZeroUsize| -> NonZeroUsize {
NonZeroUsize::new(addr.get().wrapping_add(new_offset)).unwrap()
};
let result = ptr.map_addr(f);
}

#[kani::proof_for_contract(NonNull::with_addr)]
pub fn non_null_check_with_addr() {
const SIZE: usize = 10000;
let arr: [i32; SIZE] = kani::any();
let ptr = NonNull::new(arr.as_ptr() as *mut i32).unwrap();
let new_offset: usize = kani::any_where(|&x| x <= SIZE);
let new_addr = NonZeroUsize::new(ptr.as_ptr().addr() + new_offset).unwrap();
let result = ptr.with_addr(new_addr);
}

#[kani::proof_for_contract(NonNull::sub)]
pub fn non_null_check_sub() {
const SIZE: usize = 10000;
Expand Down

0 comments on commit 9083ec2

Please sign in to comment.