diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 715e70b98ea1..c347d8b82c4a 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -526,6 +526,11 @@ impl NonNull { #[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( + (self.as_ptr().addr() as isize).checked_add(count).is_some() && + kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_offset(count)) + )] + #[ensures(|result: &Self| result.as_ptr() == self.as_ptr().wrapping_byte_offset(count))] pub const unsafe fn byte_offset(self, count: isize) -> Self { // SAFETY: the caller must uphold the safety contract for `offset` and `byte_offset` has // the same safety contract. @@ -607,6 +612,10 @@ impl NonNull { #[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() && // Ensure the offset doesn't overflow + (count == 0 || kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_byte_add(count))) // Ensure the offset is within the same allocation + )] pub const unsafe fn byte_add(self, count: usize) -> Self { // SAFETY: the caller must uphold the safety contract for `add` and `byte_add` has the same // safety contract. @@ -820,6 +829,14 @@ impl NonNull { #[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( + self.as_ptr().addr() == origin.as_ptr().addr() || + kani::mem::same_allocation(self.as_ptr() as *const(), origin.as_ptr() as *const()) + )] + #[ensures( + |result: &isize| + *result == (self.as_ptr() as *const u8).offset_from(origin.as_ptr() as *const u8) + )] pub const unsafe fn byte_offset_from(self, origin: NonNull) -> isize { // SAFETY: the caller must uphold the safety contract for `byte_offset_from`. unsafe { self.pointer.byte_offset_from(origin.pointer) } @@ -1736,6 +1753,7 @@ impl From<&T> for NonNull { mod verify { use super::*; use crate::ptr::null_mut; + use crate::mem; use kani::PointerGenerator; trait SampleTrait { @@ -2345,4 +2363,72 @@ mod verify { generate_write_volatile_harness!(usize, {core::mem::size_of::()}, non_null_check_write_volatile_usize); generate_write_volatile_harness!((), 1, non_null_check_write_volatile_unit); + #[kani::proof_for_contract(NonNull::byte_add)] + pub fn non_null_byte_add_proof() { + // Make size as 1000 to ensure the array is large enough to cover various senarios + // while maintaining a reasonable proof runtime + const ARR_SIZE: usize = mem::size_of::() * 1000; + let mut generator = PointerGenerator::::new(); + + let count: usize = kani::any(); + let raw_ptr: *mut i32 = generator.any_in_bounds().ptr as *mut i32; + + unsafe { + let ptr = NonNull::new(raw_ptr).unwrap(); + let result = ptr.byte_add(count); + } + } + + #[kani::proof_for_contract(NonNull::byte_add)] + pub fn non_null_byte_add_dangling_proof() { + let ptr = NonNull::::dangling(); + unsafe { + let _ = ptr.byte_add(0); + } + } + + #[kani::proof_for_contract(NonNull::byte_offset)] + pub fn non_null_byte_offset_proof() { + const ARR_SIZE: usize = mem::size_of::() * 1000; + let mut generator = PointerGenerator::::new(); + + let count: isize = kani::any(); + let raw_ptr: *mut i32 = generator.any_in_bounds().ptr as *mut i32; + + unsafe { + let ptr = NonNull::new(raw_ptr).unwrap(); + let result = ptr.byte_offset(count); + } + } + + #[kani::proof_for_contract(NonNull::byte_offset_from)] + pub fn non_null_byte_offset_from_proof() { + const SIZE: usize = mem::size_of::() * 1000; + let mut generator1 = PointerGenerator::::new(); + let mut generator2 = PointerGenerator::::new(); + + let ptr: *mut i32 = generator1.any_in_bounds().ptr as *mut i32; + + let origin: *mut i32 = if kani::any() { + generator1.any_in_bounds().ptr as *mut i32 + } else { + generator2.any_in_bounds().ptr as *mut i32 + }; + + let ptr_nonnull = unsafe { NonNull::new(ptr).unwrap() }; + let origin_nonnull = unsafe { NonNull::new(origin).unwrap() }; + + unsafe { + let result = ptr_nonnull.byte_offset_from(origin_nonnull); + } + } + + #[kani::proof_for_contract(NonNull::byte_offset_from)] + pub fn non_null_byte_offset_from_dangling_proof() { + let origin = NonNull::::dangling(); + let ptr = origin; + unsafe { + let _ = ptr.byte_offset_from(origin); + } + } }