Skip to content

Commit 30b689e

Browse files
Use addr_eq in NonNull contracts to support wide pointers
Several postconditions in NonNull compared raw pointers with `==` or `core::ptr::eq`: as_ptr, new, new_unchecked, the slice as_mut_ptr, and - via ptr::eq - as_ref, as_mut, as_uninit_ref and as_uninit_mut. For wide pointers (T: ?Sized with dyn metadata) such comparisons also compare vtable pointers, whose identity is unspecified in Rust; Kani rejects them with "Reached unstable vtable comparison 'Eq'". With dependency contracts asserted (the Kani default since model-checking/kani#3802), any harness whose call graph evaluates these clauses on a trait-object NonNull fails, e.g. ptr::non_null::verify::non_null_check_from_raw_part_trait (the comparison surfaces in ptr::eq::<dyn SampleTrait>, reached from as_ref's postcondition). Compare with core::ptr::addr_eq instead, which is well-defined for any pointer types. All these functions produce their result directly from `self`, so metadata is preserved by construction, and the accompanying comments already described the intent as address preservation. The casts in the as_uninit_* clauses need explicit turbofish types now that the comparison no longer constrains their type parameter. Verified (Kani 152c6a8c + CBMC 6.10.0): non_null_check_from_raw_part_trait now passes with contracts asserted - this was the last remaining verdict difference on a 125-harness sample between runs with and without --no-assert-contracts. The non_null_check_{as_ref,as_mut,as_uninit*,from_raw_part*,as_ptr,new} harnesses pass in both configurations, with one exception: non_null_check_as_uninit_slice_mut fails with contracts asserted both with and without this change (a pre-existing dereference/alignment issue reached via asserted contracts, tracked separately). Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
1 parent 6503980 commit 30b689e

1 file changed

Lines changed: 23 additions & 10 deletions

File tree

library/core/src/ptr/non_null.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl<T: Sized> NonNull<T> {
174174
#[must_use]
175175
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
176176
#[requires(ub_checks::can_dereference(self.as_ptr()))] // Ensure the pointer is valid to create a reference.
177-
#[ensures(|result: &&MaybeUninit<T>| core::ptr::eq(*result, self.cast().as_ptr()))] // Ensure returned reference points to the correct memory location.
177+
#[ensures(|result: &&MaybeUninit<T>| core::ptr::addr_eq(*result, self.cast::<MaybeUninit<T>>().as_ptr()))] // Ensure returned reference points to the correct memory location.
178178
pub const unsafe fn as_uninit_ref<'a>(self) -> &'a MaybeUninit<T> {
179179
// SAFETY: the caller must guarantee that `self` meets all the
180180
// requirements for a reference.
@@ -199,7 +199,7 @@ impl<T: Sized> NonNull<T> {
199199
#[must_use]
200200
#[unstable(feature = "ptr_as_uninit", issue = "75402")]
201201
#[requires(ub_checks::can_dereference(self.as_ptr()))] // Ensure pointer is valid to create a mutable reference.
202-
#[ensures(|result: &&mut MaybeUninit<T>| core::ptr::eq(*result, self.cast().as_ptr()))] // Ensure the returned reference points to the correct memory.
202+
#[ensures(|result: &&mut MaybeUninit<T>| core::ptr::addr_eq(*result, self.cast::<MaybeUninit<T>>().as_ptr()))] // Ensure the returned reference points to the correct memory.
203203
pub const unsafe fn as_uninit_mut<'a>(self) -> &'a mut MaybeUninit<T> {
204204
// SAFETY: the caller must guarantee that `self` meets all the
205205
// requirements for a reference.
@@ -243,7 +243,8 @@ impl<T: PointeeSized> NonNull<T> {
243243
#[inline]
244244
#[track_caller]
245245
#[requires(!ptr.is_null())]
246-
#[ensures(|result| result.as_ptr() == ptr)]
246+
// See as_ptr regarding the use of addr_eq for wide-pointer support.
247+
#[ensures(|result| core::ptr::addr_eq(result.as_ptr(), ptr))]
247248
pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
248249
// SAFETY: the caller must guarantee that `ptr` is non-null.
249250
unsafe {
@@ -281,7 +282,8 @@ impl<T: PointeeSized> NonNull<T> {
281282
#[rustc_const_stable(feature = "const_nonnull_new", since = "1.85.0")]
282283
#[inline]
283284
#[ensures(|result| result.is_some() == !ptr.is_null())]
284-
#[ensures(|result| result.is_none() || result.expect("ptr is null!").as_ptr() == ptr)]
285+
// See as_ptr regarding the use of addr_eq for wide-pointer support.
286+
#[ensures(|result| result.is_none() || core::ptr::addr_eq(result.expect("ptr is null!").as_ptr(), ptr))]
285287
pub const fn new(ptr: *mut T) -> Option<Self> {
286288
if !ptr.is_null() {
287289
// SAFETY: The pointer is already checked and is not null
@@ -420,8 +422,13 @@ impl<T: PointeeSized> NonNull<T> {
420422
#[rustc_never_returns_null_ptr]
421423
#[must_use]
422424
#[inline(always)]
423-
//Ensures address of resulting pointer is same as original
424-
#[ensures(|result: &*mut T| *result == self.pointer as *mut T)]
425+
// Ensures the address of the resulting pointer is the same as the
426+
// original. `addr_eq` (rather than `==`) makes this well-defined for
427+
// wide pointers too: comparing `*mut dyn Trait` with `==` also compares
428+
// vtable pointers, whose identity is unspecified (and which Kani rejects
429+
// with "unstable vtable comparison"). `as_ptr` is a representation-level
430+
// conversion that trivially preserves metadata.
431+
#[ensures(|result: &*mut T| core::ptr::addr_eq(*result, self.pointer))]
425432
pub const fn as_ptr(self) -> *mut T {
426433
// This is a transmute for the same reasons as `NonZero::get`.
427434

@@ -462,7 +469,12 @@ impl<T: PointeeSized> NonNull<T> {
462469
#[must_use]
463470
#[inline(always)]
464471
#[requires(ub_checks::can_dereference(self.as_ptr() as *const()))] // Ensure input is convertible to a reference
465-
#[ensures(|result: &&T| core::ptr::eq(*result, self.as_ptr()))] // Ensure returned reference matches pointer
472+
// addr_eq (rather than ptr::eq) so the clause is well-defined for
473+
// wide pointers too: comparing *const dyn with == also compares vtable
474+
// pointers, whose identity is unspecified (Kani: "unstable vtable
475+
// comparison"). The reference is created from `self`, so metadata is
476+
// preserved by construction.
477+
#[ensures(|result: &&T| core::ptr::addr_eq(*result, self.as_ptr()))] // Ensure returned reference matches pointer
466478
pub const unsafe fn as_ref<'a>(&self) -> &'a T {
467479
// SAFETY: the caller must guarantee that `self` meets all the
468480
// requirements for a reference.
@@ -503,7 +515,8 @@ impl<T: PointeeSized> NonNull<T> {
503515
#[inline(always)]
504516
#[requires(ub_checks::can_dereference(self.as_ptr() as *const()))]
505517
// verify result (a mutable reference) is still associated with the same memory address as the raw pointer stored in self
506-
#[ensures(|result: &&mut T| core::ptr::eq(*result, self.as_ptr()))]
518+
// See as_ref regarding the use of addr_eq.
519+
#[ensures(|result: &&mut T| core::ptr::addr_eq(*result, self.as_ptr()))]
507520
pub const unsafe fn as_mut<'a>(&mut self) -> &'a mut T {
508521
// SAFETY: the caller must guarantee that `self` meets all the
509522
// requirements for a mutable reference.
@@ -1674,8 +1687,8 @@ impl<T> NonNull<[T]> {
16741687
#[must_use]
16751688
#[unstable(feature = "slice_ptr_get", issue = "74265")]
16761689
#[rustc_never_returns_null_ptr]
1677-
// Address preservation
1678-
#[ensures(|result: &*mut T| *result == self.pointer as *mut T)]
1690+
// Address preservation; see as_ptr regarding the use of addr_eq.
1691+
#[ensures(|result: &*mut T| core::ptr::addr_eq(*result, self.pointer))]
16791692
pub const fn as_mut_ptr(self) -> *mut T {
16801693
self.as_non_null_ptr().as_ptr()
16811694
}

0 commit comments

Comments
 (0)