Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contract and harness for copy_to, copy_to_nonoverlapping, copy_from, and copy_from_nonoverlapping #149

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
97 changes: 95 additions & 2 deletions library/core/src/ptr/non_null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::slice::{self, SliceIndex};
use crate::ub_checks::assert_unsafe_precondition;
use crate::{fmt, hash, intrinsics, ptr};
use safety::{ensures, requires};

use crate::{ub_checks};

#[cfg(kani)]
use crate::kani;
Expand Down Expand Up @@ -965,6 +965,13 @@ 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 = "const_intrinsic_copy", since = "1.83.0")]
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or(false, |size| size <= isize::MAX as usize)
&& ub_checks::can_dereference(self.as_ptr() as *const MaybeUninit<T>)
&& ub_checks::can_write(dest.as_ptr())
&& ub_checks::can_write(dest.as_ptr().add(count)))]
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
#[kani::modifies(dest.as_ptr())]
pub const unsafe fn copy_to(self, dest: NonNull<T>, count: usize)
where
T: Sized,
Expand All @@ -985,6 +992,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 = "const_intrinsic_copy", since = "1.83.0")]
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or(false, |size| size <= isize::MAX as usize)
&& ub_checks::can_dereference(self.as_ptr() as *const MaybeUninit<T>)
&& ub_checks::can_write(dest.as_ptr())
&& ub_checks::can_write(dest.as_ptr().add(count))
&& (self.as_ptr() as usize).abs_diff(dest.as_ptr() as usize) >= 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))]
#[kani::modifies(dest.as_ptr())]
Comment on lines +1000 to +1002

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
#[kani::modifies(dest.as_ptr())]
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
#[kani::modifies(dest.as_ptr())]

pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull<T>, count: usize)
where
T: Sized,
Expand All @@ -1005,6 +1020,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 = "const_intrinsic_copy", since = "1.83.0")]
#[requires (count.checked_mul(core::mem::size_of::<T>()).map_or(false, |size| size <= isize::MAX as usize
&& ub_checks::can_dereference(src.as_ptr() as *const MaybeUninit<T>)
&& ub_checks::can_write(self.as_ptr())
&& ub_checks::can_write (self.as_ptr().add(count)))
&& (src.as_ptr() as usize).abs_diff(self.as_ptr() as usize) >= 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()))]
#[kani::modifies(self.as_ptr())]
pub const unsafe fn copy_from(self, src: NonNull<T>, count: usize)
where
T: Sized,
Expand All @@ -1025,6 +1048,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 = "const_intrinsic_copy", since = "1.83.0")]
#[requires (count.checked_mul(core::mem::size_of::<T>()).map_or(false, |size| size <= isize::MAX as usize
&& ub_checks::can_dereference(src.as_ptr() as *const MaybeUninit<T>)
&& ub_checks::can_write(self.as_ptr())
&& ub_checks::can_write(self.as_ptr().add(count)))
&& (self.as_ptr() as usize).abs_diff(src.as_ptr() as usize) >= count * core::mem::size_of::<T>())]//The source and destination regions do not overlap.
#[ensures (|result: &()| ub_checks::can_dereference(src.as_ptr() as *const u8)
&& ub_checks::can_dereference(self.as_ptr()))]
#[kani::modifies(self.as_ptr())]
pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull<T>, count: usize)
where
T: Sized,
Expand Down Expand Up @@ -1811,6 +1842,7 @@ impl<T: ?Sized> From<&T> for NonNull<T> {
mod verify {
use super::*;
use crate::ptr::null_mut;
use kani::PointerGenerator;

// pub const unsafe fn new_unchecked(ptr: *mut T) -> Self
#[kani::proof_for_contract(NonNull::new_unchecked)]
Expand All @@ -1826,7 +1858,68 @@ mod verify {
pub fn non_null_check_new() {
let mut x: i32 = kani::any();
let xptr = &mut x;
let maybe_null_ptr = if kani::any() { xptr as *mut i32 } else { null_mut() };
let maybe_null_ptr = if kani::any() { xptr as *mut i32 } else { null_mut() };
let _ = NonNull::new(maybe_null_ptr);
}

#[kani::proof_for_contract(NonNull::<T>::copy_to)]
pub fn non_null_check_copy_to() {
// PointerGenerator instance
let mut generator = PointerGenerator::<16>::new();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How the capacity of the generator affect the running time of the proof? Can we use the maximum capacity, e.g., isize::MAX / size_of::<i32>() here?

// 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() {
// Create source and destination values
let mut src_value: i32 = kani::any();
let mut dest_value: i32 = 0;

// Create NonNull pointers
let src = NonNull::new(&mut src_value as *mut i32).unwrap();
let dest = NonNull::new(&mut dest_value as *mut i32).unwrap();

//count is 1 as working with single i32 value
let count = 1;
unsafe {
src.copy_from(dest, count);
}
}
#[kani::proof_for_contract(NonNull::<T>::copy_to_nonoverlapping)]
pub fn non_null_check_copy_to_nonoverlapping() {
let mut src_value: i32 = kani::any();
let mut dest_value: i32 = 0;

let src = NonNull::new(&mut src_value as *mut i32).unwrap();
let dest = NonNull::new(&mut dest_value as *mut i32).unwrap();

//count represents no of elements to copy
let count = 1;
unsafe {
src.copy_to_nonoverlapping(dest, count);
}
}
#[kani::proof_for_contract(NonNull::<T>::copy_from_nonoverlapping)]
pub fn non_null_check_copy_from_nonoverlapping() {
let mut src_value: i32 = kani::any();
let mut dest_value: i32 = 0;

let src = NonNull::new(&mut src_value as *mut i32).unwrap();
let dest = NonNull::new(&mut dest_value as *mut i32).unwrap();
Dhvani-Kapadia marked this conversation as resolved.
Show resolved Hide resolved

//count represents no of elements to copy
let count = 1;

unsafe {
dest.copy_from_nonoverlapping(src, count);
}
}
}
Loading