Skip to content

Commit 94a31f5

Browse files
Contracts & Harnesses for NonNull::read, NonNull::read_volatile, NonNull::read_unaligned (#156)
Towards #52 Contracts & Harnesses for `NonNull::read`, `NonNull::read_volatile`, `NonNull::read_unaligned` ### Discussion For the ensures clause in the contract for `NonNull::read`, I could not directly compare the value pointed by self and result due to unsized and lack of PartialEq implementation, and the comparison is currently moved to the harness. ### Verification Successful verification: ``` SUMMARY: ** 0 of 141 failed VERIFICATION:- SUCCESSFUL Verification Time: 0.1760525s Complete - 5 successfully verified harnesses, 0 failures, 5 total. ``` 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: Zyad Hassan <zyadh@amazon.com>
1 parent 20d5a0b commit 94a31f5

File tree

1 file changed

+88
-10
lines changed

1 file changed

+88
-10
lines changed

library/core/src/ptr/non_null.rs

Lines changed: 88 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use safety::{ensures, requires};
1313

1414
#[cfg(kani)]
1515
use crate::kani;
16+
#[cfg(kani)]
17+
use crate::ub_checks;
1618

1719
/// `*mut T` but non-zero and [covariant].
1820
///
@@ -568,7 +570,7 @@ impl<T: ?Sized> NonNull<T> {
568570
#[must_use = "returns a new pointer rather than modifying its argument"]
569571
#[stable(feature = "non_null_convenience", since = "1.80.0")]
570572
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
571-
#[requires(count.checked_mul(core::mem::size_of::<T>()).is_some()
573+
#[requires(count.checked_mul(core::mem::size_of::<T>()).is_some()
572574
&& count * core::mem::size_of::<T>() <= isize::MAX as usize
573575
&& (self.pointer as isize).checked_add(count as isize * core::mem::size_of::<T>() as isize).is_some() // check wrapping add
574576
&& kani::mem::same_allocation(self.pointer, self.pointer.wrapping_offset(count as isize)))]
@@ -910,6 +912,7 @@ impl<T: ?Sized> NonNull<T> {
910912
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
911913
#[stable(feature = "non_null_convenience", since = "1.80.0")]
912914
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
915+
#[requires(ub_checks::can_dereference(self.pointer))]
913916
pub const unsafe fn read(self) -> T
914917
where
915918
T: Sized,
@@ -931,6 +934,7 @@ impl<T: ?Sized> NonNull<T> {
931934
#[inline]
932935
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
933936
#[stable(feature = "non_null_convenience", since = "1.80.0")]
937+
#[requires(ub_checks::can_dereference(self.pointer))]
934938
pub unsafe fn read_volatile(self) -> T
935939
where
936940
T: Sized,
@@ -951,6 +955,7 @@ impl<T: ?Sized> NonNull<T> {
951955
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
952956
#[stable(feature = "non_null_convenience", since = "1.80.0")]
953957
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
958+
#[requires(ub_checks::can_read_unaligned(self.pointer))]
954959
pub const unsafe fn read_unaligned(self) -> T
955960
where
956961
T: Sized,
@@ -1219,9 +1224,9 @@ impl<T: ?Sized> NonNull<T> {
12191224
let stride = crate::mem::size_of::<T>();
12201225
// ZSTs
12211226
if stride == 0 {
1222-
if self.pointer.addr() % align == 0 {
1227+
if self.pointer.addr() % align == 0 {
12231228
return *result == 0;
1224-
} else {
1229+
} else {
12251230
return *result == usize::MAX;
12261231
}
12271232
}
@@ -1233,8 +1238,8 @@ impl<T: ?Sized> NonNull<T> {
12331238
// requires computing gcd(a, stride), which is too expensive without
12341239
// quantifiers (https://model-checking.github.io/kani/rfc/rfcs/0010-quantifiers.html).
12351240
// This should be updated once quantifiers are available.
1236-
if (align % stride != 0 && *result == usize::MAX) {
1237-
return true;
1241+
if (align % stride != 0 && *result == usize::MAX) {
1242+
return true;
12381243
}
12391244
// If we reach this case, either:
12401245
// - align % stride == 0 and self.pointer.addr() % stride == 0, so it is definitely possible to align the pointer
@@ -1867,6 +1872,79 @@ mod verify {
18671872
let _ = NonNull::new(maybe_null_ptr);
18681873
}
18691874

1875+
// pub const unsafe fn read(self) -> T where T: Sized
1876+
#[kani::proof_for_contract(NonNull::read)]
1877+
pub fn non_null_check_read() {
1878+
let ptr_u8: *mut u8 = &mut kani::any();
1879+
let nonnull_ptr_u8 = NonNull::new(ptr_u8).unwrap();
1880+
unsafe {
1881+
let result = nonnull_ptr_u8.read();
1882+
kani::assert(*ptr_u8 == result, "read returns the correct value");
1883+
}
1884+
1885+
// array example
1886+
const ARR_LEN: usize = 10000;
1887+
let mut generator = PointerGenerator::<ARR_LEN>::new();
1888+
let raw_ptr: *mut i8 = generator.any_in_bounds().ptr;
1889+
let nonnull_ptr = unsafe { NonNull::new(raw_ptr).unwrap()};
1890+
unsafe {
1891+
let result = nonnull_ptr.read();
1892+
kani::assert( *nonnull_ptr.as_ptr() == result, "read returns the correct value");
1893+
}
1894+
}
1895+
1896+
// pub unsafe fn read_volatile(self) -> T where T: Sized
1897+
#[kani::proof_for_contract(NonNull::read_volatile)]
1898+
pub fn non_null_check_read_volatile() {
1899+
let ptr_u8: *mut u8 = &mut kani::any();
1900+
let nonnull_ptr_u8 = NonNull::new(ptr_u8).unwrap();
1901+
unsafe {
1902+
let result = nonnull_ptr_u8.read_volatile();
1903+
kani::assert(*ptr_u8 == result, "read returns the correct value");
1904+
}
1905+
1906+
// array example
1907+
const ARR_LEN: usize = 10000;
1908+
let mut generator = PointerGenerator::<ARR_LEN>::new();
1909+
let raw_ptr: *mut i8 = generator.any_in_bounds().ptr;
1910+
let nonnull_ptr = unsafe { NonNull::new(raw_ptr).unwrap()};
1911+
unsafe {
1912+
let result = nonnull_ptr.read_volatile();
1913+
kani::assert( *nonnull_ptr.as_ptr() == result, "read returns the correct value");
1914+
}
1915+
}
1916+
1917+
#[repr(packed, C)]
1918+
struct Packed {
1919+
_padding: u8,
1920+
unaligned: u32,
1921+
}
1922+
1923+
// pub const unsafe fn read_unaligned(self) -> T where T: Sized
1924+
#[kani::proof_for_contract(NonNull::read_unaligned)]
1925+
pub fn non_null_check_read_unaligned() {
1926+
// unaligned pointer
1927+
let mut generator = PointerGenerator::<10000>::new();
1928+
let unaligned_ptr: *mut u8 = generator.any_in_bounds().ptr;
1929+
let unaligned_nonnull_ptr = NonNull::new(unaligned_ptr).unwrap();
1930+
unsafe {
1931+
let result = unaligned_nonnull_ptr.read_unaligned();
1932+
kani::assert( *unaligned_nonnull_ptr.as_ptr() == result, "read returns the correct value");
1933+
}
1934+
1935+
// read an unaligned value from a packed struct
1936+
let unaligned_value: u32 = kani::any();
1937+
let packed = Packed {
1938+
_padding: kani::any::<u8>(),
1939+
unaligned: unaligned_value,
1940+
};
1941+
1942+
let unaligned_ptr = ptr::addr_of!(packed.unaligned);
1943+
let nonnull_packed_ptr = NonNull::new(unaligned_ptr as *mut u32).unwrap();
1944+
let v = unsafe { nonnull_packed_ptr.read_unaligned() };
1945+
assert_eq!(v, unaligned_value);
1946+
}
1947+
18701948
// pub const unsafe fn add(self, count: usize) -> Self
18711949
#[kani::proof_for_contract(NonNull::add)]
18721950
pub fn non_null_check_add() {
@@ -1875,16 +1953,16 @@ mod verify {
18751953
let raw_ptr: *mut i8 = generator.any_in_bounds().ptr;
18761954
let ptr = unsafe { NonNull::new(raw_ptr).unwrap()};
18771955
// Create a non-deterministic count value
1878-
let count: usize = kani::any();
1879-
1956+
let count: usize = kani::any();
1957+
18801958
unsafe {
18811959
let result = ptr.add(count);
18821960
}
18831961
}
18841962

18851963
// pub fn addr(self) -> NonZero<usize>
18861964
#[kani::proof_for_contract(NonNull::addr)]
1887-
pub fn non_null_check_addr() {
1965+
pub fn non_null_check_addr() {
18881966
// Create NonNull pointer & get pointer address
18891967
let x = kani::any::<usize>() as *mut i32;
18901968
let Some(nonnull_xptr) = NonNull::new(x) else { return; };
@@ -1897,7 +1975,7 @@ mod verify {
18971975
// Create NonNull pointer
18981976
let x = kani::any::<usize>() as *mut i32;
18991977
let Some(nonnull_xptr) = NonNull::new(x) else { return; };
1900-
1978+
19011979
// Call align_offset with valid align value
19021980
let align: usize = kani::any();
19031981
kani::assume(align.is_power_of_two());
@@ -1914,7 +1992,7 @@ mod verify {
19141992

19151993
// Generate align value that is not necessarily a power of two
19161994
let invalid_align: usize = kani::any();
1917-
1995+
19181996
// Trigger panic
19191997
let offset = nonnull_xptr.align_offset(invalid_align);
19201998
}

0 commit comments

Comments
 (0)