Skip to content

Commit

Permalink
Contract and harness for copy_to, copy_to_nonoverlapping, copy_from, …
Browse files Browse the repository at this point in the history
…and copy_from_nonoverlapping (#149)

Description

This PR includes contracts and proof harnesses for the four APIs
copy_to, copy_to_nonoverlapping, copy_from, and copy_from_nonoverlapping
which are part of the NonNull library in Rust.

Changes Overview:

Covered APIs:
NonNull::copy_to
NonNull::copy_to_nonoverlapping
NonNull::copy_from
NonNull::opy_from_nonoverlapping

Proof harness:
non_null_check_copy_to
non_null_check_copy_to_nonoverlapping
non_null_check_copy_from
non_null_check_copy_from_nonoverlapping,

Revalidation

To revalidate the verification results, run path_to/kani/scripts/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:

SUMMARY:
 ** 0 of 141 failed

VERIFICATION:- SUCCESSFUL
Verification Time: 0.62114185s

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: Qinyuan Wu <qinyuanw@andrew.cmu.edu>
Co-authored-by: Carolyn Zech <cmzech@amazon.com>
Co-authored-by: Qinyuan Wu <53478459+QinyuanWu@users.noreply.github.com>
Co-authored-by: Michael Tautschnig <mt@debian.org>
  • Loading branch information
5 people authored Dec 12, 2024
1 parent 27a9931 commit ea7a95f
Showing 1 changed file with 84 additions and 0 deletions.
84 changes: 84 additions & 0 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1035,8 +1035,14 @@ impl<T: ?Sized> NonNull<T> {
/// [`ptr::copy`]: crate::ptr::copy()
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(dest, count).as_ptr()))]
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(self, count).as_ptr())
&& ub_checks::can_write(NonNull::slice_from_raw_parts(dest, count).as_ptr()))]
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
pub const unsafe fn copy_to(self, dest: NonNull<T>, count: usize)
where
T: Sized,
Expand All @@ -1055,8 +1061,15 @@ impl<T: ?Sized> NonNull<T> {
/// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping()
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(dest, count).as_ptr()))]
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(self, count).as_ptr())
&& ub_checks::can_write(NonNull::slice_from_raw_parts(dest, count).as_ptr())
&& ub_checks::maybe_is_nonoverlapping(self.as_ptr() as *const (), dest.as_ptr() as *const (), count, core::mem::size_of::<T>()))]
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull<T>, count: usize)
where
T: Sized,
Expand All @@ -1075,8 +1088,14 @@ impl<T: ?Sized> NonNull<T> {
/// [`ptr::copy`]: crate::ptr::copy()
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(self, count).as_ptr()))]
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(src, count).as_ptr())
&& ub_checks::can_write(NonNull::slice_from_raw_parts(self, count).as_ptr()))]
#[ensures(|result: &()| ub_checks::can_dereference(src.as_ptr() as *const u8)
&& ub_checks::can_dereference(self.as_ptr() as *const u8))]
pub const unsafe fn copy_from(self, src: NonNull<T>, count: usize)
where
T: Sized,
Expand All @@ -1095,8 +1114,15 @@ impl<T: ?Sized> NonNull<T> {
/// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping()
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(self, count).as_ptr()))]
#[stable(feature = "non_null_convenience", since = "1.80.0")]
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(src, count).as_ptr())
&& ub_checks::can_write(NonNull::slice_from_raw_parts(self, count).as_ptr())
&& ub_checks::maybe_is_nonoverlapping(src.as_ptr() as *const (), self.as_ptr() as *const (), count, core::mem::size_of::<T>()))]
#[ensures(|result: &()| ub_checks::can_dereference(src.as_ptr() as *const u8)
&& ub_checks::can_dereference(self.as_ptr() as *const u8))]
pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull<T>, count: usize)
where
T: Sized,
Expand Down Expand Up @@ -2538,4 +2564,62 @@ mod verify {
let _ = ptr.byte_offset_from(origin);
}
}

#[kani::proof_for_contract(NonNull::<T>::copy_to)]
pub fn non_null_check_copy_to() {
// PointerGenerator instance
let mut generator = PointerGenerator::<16>::new();
// Generate arbitrary pointers for src and dest
let src_ptr = generator.any_in_bounds::<i32>().ptr;
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
// Wrap the raw pointers in NonNull
let src = NonNull::new(src_ptr).unwrap();
let dest = NonNull::new(dest_ptr).unwrap();
// Generate an arbitrary count using kani::any
let count: usize = kani::any();
unsafe { src.copy_to(dest, count);}
}

#[kani::proof_for_contract(NonNull::<T>::copy_from)]
pub fn non_null_check_copy_from() {
// PointerGenerator instance
let mut generator = PointerGenerator::<16>::new();
// Generate arbitrary pointers for src and dest
let src_ptr = generator.any_in_bounds::<i32>().ptr;
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
// Wrap the raw pointers in NonNull
let src = NonNull::new(src_ptr).unwrap();
let dest = NonNull::new(dest_ptr).unwrap();
// Generate an arbitrary count using kani::any
let count: usize = kani::any();
unsafe { src.copy_from(dest, count);}
}
#[kani::proof_for_contract(NonNull::<T>::copy_to_nonoverlapping)]
pub fn non_null_check_copy_to_nonoverlapping() {
// PointerGenerator instance
let mut generator = PointerGenerator::<16>::new();
// Generate arbitrary pointers for src and dest
let src_ptr = generator.any_in_bounds::<i32>().ptr;
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
// Wrap the raw pointers in NonNull
let src = NonNull::new(src_ptr).unwrap();
let dest = NonNull::new(dest_ptr).unwrap();
// Generate an arbitrary count using kani::any
let count: usize = kani::any();
unsafe { src.copy_to_nonoverlapping(dest, count);}
}
#[kani::proof_for_contract(NonNull::<T>::copy_from_nonoverlapping)]
pub fn non_null_check_copy_from_nonoverlapping() {
// PointerGenerator instance
let mut generator = PointerGenerator::<16>::new();
// Generate arbitrary pointers for src and dest
let src_ptr = generator.any_in_bounds::<i32>().ptr;
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
// Wrap the raw pointers in NonNull
let src = NonNull::new(src_ptr).unwrap();
let dest = NonNull::new(dest_ptr).unwrap();
// Generate an arbitrary count using kani::any
let count: usize = kani::any();
unsafe { src.copy_from_nonoverlapping(dest, count);}
}
}

0 comments on commit ea7a95f

Please sign in to comment.