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

Contracts and Harnesses for <*const T>::add, sub and offset #166

Merged
merged 61 commits into from
Dec 3, 2024
Merged
Changes from 46 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
500f249
Add verification contract for *const T::add and *const T::add
szlee118 Sep 13, 2024
99701ff
Update const_ptr.rs
xsxszab Sep 19, 2024
31c82a9
Merge branch 'model-checking:main' into test
stogaru Sep 19, 2024
f84b7a6
Changes generic type to i32 in proof.
stogaru Sep 19, 2024
a839820
Remove unneeded file
szlee118 Sep 20, 2024
21e0ab8
added comments for function contracts and harnesses
Sep 20, 2024
b54ce9e
Merge branch 'main' into verify/ptr_const_offset
stogaru Sep 23, 2024
0c0078f
reverted library code format change and removed unnecessary comments
xsxszab Sep 30, 2024
a192129
added macros for verifying all integer types for offset and add
xsxszab Oct 1, 2024
2ef5923
implemented function contract and integer type proofs for fn sub
xsxszab Oct 1, 2024
f1602aa
Add verification of slice type proofs for fn offset
szlee118 Oct 1, 2024
1b467d5
add TODOs for proof for contracts
xsxszab Oct 1, 2024
9461027
verified *mut T::offset for all integer types
xsxszab Oct 1, 2024
159137b
Add verification of slice type proofs for fn add and sub
szlee118 Oct 3, 2024
df56d18
Remove commented code
szlee118 Oct 3, 2024
eab0a15
removed unnecessary comments
xsxszab Oct 3, 2024
e07040f
reverted changes to mut_ptr.rs
xsxszab Oct 3, 2024
0ff52b5
removed necessary comments
xsxszab Oct 3, 2024
ccd5c3c
aligned format for mut_ptr.rs
xsxszab Oct 3, 2024
180a276
aligned format for mut_ptr.rs
xsxszab Oct 3, 2024
6a85e3e
Merge pull request #1 from stogaru/verify/ptr_const_integer_types
xsxszab Oct 3, 2024
6853284
Merge branch 'model-checking:main' into verify/ptr_const_offset
xsxszab Oct 3, 2024
fb79caf
Adds proofs for composite type - tuple
stogaru Oct 3, 2024
7d05c96
Change function proof names
stogaru Oct 3, 2024
8e49dcd
Merge pull request #2 from stogaru/verify/ptr_const_composite
stogaru Oct 3, 2024
9af0e39
Merge branch 'verify/ptr_const_offset' into verify/ptr_const_slice_type
szlee118 Oct 3, 2024
31532c9
Merge pull request #4 from stogaru/verify/ptr_const_slice_type
szlee118 Oct 3, 2024
f9f5371
Added changes for *const T::add & *const::sub verifying unit type
MayureshJoshi25 Oct 7, 2024
eab900e
Updated proofs for const ptr (unit type)
MayureshJoshi25 Oct 12, 2024
05c0fdd
Merge branch 'verify/ptr_const' into verify/ptr_const_unit_type
stogaru Oct 17, 2024
098f171
Merge pull request #5 from stogaru/verify/ptr_const_unit_type
stogaru Oct 17, 2024
8c75da8
Merge branch 'model-checking:main' into verify/ptr_const
stogaru Oct 18, 2024
bd418a4
Generate slice harness for array
szlee118 Oct 25, 2024
e05b5f8
Remove redundant declaration
szlee118 Oct 25, 2024
b944ec0
Update to offset test_ptr non-deterministically within the allocated …
szlee118 Nov 7, 2024
054d887
Update the precondition for offset in harness
szlee118 Nov 7, 2024
de6ef30
updated function contracts and proof for contracts for add(), sub() a…
xsxszab Nov 7, 2024
2bc0a0f
Merge branch 'verify/ptr_const' into verify/ptr_const_slice_types
szlee118 Nov 8, 2024
554e003
Modify document
szlee118 Nov 8, 2024
a69688f
Fix indentation error
szlee118 Nov 8, 2024
b18ee62
Merge pull request #14 from stogaru/verify/ptr_const_slice_types
szlee118 Nov 8, 2024
3effadd
improved comments and function naming
xsxszab Nov 14, 2024
c3cb7fe
fixed a typo
xsxszab Nov 14, 2024
949eb9c
Merge branch 'main' into verify/ptr_const
stogaru Nov 14, 2024
cc5d490
updated proofs using pointer generator
xsxszab Nov 15, 2024
ef71817
Merge branch 'main' into verify/ptr_const
xsxszab Nov 15, 2024
5edd16e
Reformatted code using rustfmt.
xsxszab Nov 27, 2024
dd2a8eb
Reverted unnecessary rustfmt formatting
xsxszab Nov 27, 2024
7b83329
Merge branch 'main' into verify/ptr_const
szlee118 Nov 28, 2024
576483f
Resolved compilation error after previous merge.
xsxszab Nov 28, 2024
330f06b
Enhanced comments to clarify proof for contracts.
xsxszab Nov 28, 2024
43a456b
Refactored macro for generating slice type verifications to reduce du…
xsxszab Nov 29, 2024
90fc86a
Fixed proof naming issue in previous commit; Refactored the macro for…
xsxszab Nov 29, 2024
9a2ee76
Formatted code using rustfmt.
xsxszab Nov 29, 2024
d783dac
Fixed function naming issues after refactoring.
xsxszab Nov 29, 2024
d7ee4d0
Fixed issues in comments
xsxszab Nov 29, 2024
6ee7941
Refactored contracts to improve performance
xsxszab Dec 2, 2024
0573c74
Replaced kani::mem::same_allocation with core::ub_checks::same_alloca…
xsxszab Dec 3, 2024
81afaf9
Rearranged proofs, moving the unit type proof to the top
xsxszab Dec 3, 2024
36ef494
Refactored function contracts for better readability.
xsxszab Dec 3, 2024
b213847
Merge branch 'main' into verify/ptr_const
tautschnig Dec 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
264 changes: 264 additions & 0 deletions library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use crate::cmp::Ordering::{Equal, Greater, Less};
use crate::intrinsics::const_eval_select;
use crate::mem::SizedTypeProperties;
use crate::slice::{self, SliceIndex};
use safety::{ensures, requires};

#[cfg(kani)]
use crate::kani;

impl<T: ?Sized> *const T {
/// Returns `true` if the pointer is null.
Expand Down Expand Up @@ -405,6 +409,20 @@ impl<T: ?Sized> *const T {
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
count.checked_mul(core::mem::size_of::<T>() as isize).is_some() &&
// Precondition 2: adding the computed offset to `self` does not cause overflow
(self as isize).checked_add((count * core::mem::size_of::<T>() as isize)).is_some() &&
feliperodri marked this conversation as resolved.
Show resolved Hide resolved
tautschnig marked this conversation as resolved.
Show resolved Hide resolved
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
// Otherwise, for non-unit types, `self` and `self.wrapping_offset(count)` should point to the same allocated object,
// restricting `count` to prevent crossing allocation boundaries.
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_offset(count))))
)]
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
// verifying that the result remains within the same allocation as `self`.
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self, *result as *const T))]
pub const unsafe fn offset(self, count: isize) -> *const T
where
T: Sized,
Expand Down Expand Up @@ -918,6 +936,21 @@ impl<T: ?Sized> *const T {
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
// Precondition 2: adding the computed offset to `self` does not cause overflow
(self as isize).checked_add((count * core::mem::size_of::<T>()) as isize).is_some() &&
tautschnig marked this conversation as resolved.
Show resolved Hide resolved
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
// Otherwise, for non-unit types, `self` and `self.wrapping_add(count)` should point to the same allocated object,
// restricting `count` to prevent crossing allocation boundaries.
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_add(count))))
)]
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
// verifying that the result remains within the same allocation as `self`.
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self, *result as *const T))]
pub const unsafe fn add(self, count: usize) -> Self
where
T: Sized,
Expand Down Expand Up @@ -1027,6 +1060,21 @@ impl<T: ?Sized> *const T {
#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))]
#[inline(always)]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
// Precondition 2: subtracting the computed offset from `self` does not cause overflow
(self as isize).checked_sub((count * core::mem::size_of::<T>()) as isize).is_some() &&
tautschnig marked this conversation as resolved.
Show resolved Hide resolved
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
// Otherwise, for non-unit types, `self` and `self.wrapping_sub(count)` should point to the same allocated object,
// restricting `count` to prevent crossing allocation boundaries.
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_sub(count))))
)]
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
// verifying that the result remains within the same allocation as `self`.
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self, *result as *const T))]
pub const unsafe fn sub(self, count: usize) -> Self
where
T: Sized,
Expand Down Expand Up @@ -1899,3 +1947,219 @@ impl<T: ?Sized> PartialOrd for *const T {
*self >= *other
}
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use crate::kani;
use core::mem;
// Constant for array size used in all tests, for performance reason
tautschnig marked this conversation as resolved.
Show resolved Hide resolved
const ARRAY_SIZE: usize = 5;

/// This macro generates verification harnesses for the `offset`, `add`, and `sub`
/// pointer operations for a slice type and function name.
/// - `$ty`: The type of the array (e.g., i32, u32, tuples).
/// - `$offset_fn`: The function name for the `offset` operation.
/// - `$add_fn`: The function name for the `add` operation.
/// - `$sub_fn`: The function name for the `sub` operation.
macro_rules! generate_slice_harnesses {
($ty:ty, $offset_fn:ident, $add_fn:ident, $sub_fn:ident) => {
// Generates a harness for the `offset` operation
#[kani::proof_for_contract(<*const $ty>::offset)]
fn $offset_fn() {
let arr: [$ty; ARRAY_SIZE] = kani::Arbitrary::any_array();
let test_ptr: *const $ty = arr.as_ptr();
let offset: usize = kani::any();
let count: isize = kani::any();
kani::assume(offset <= ARRAY_SIZE * mem::size_of::<$ty>());
let ptr_with_offset: *const $ty = test_ptr.wrapping_byte_add(offset);
unsafe {
ptr_with_offset.offset(count);
}
}

// Generates a harness for the `add` operation
#[kani::proof_for_contract(<*const $ty>::add)]
fn $add_fn() {
let arr: [$ty; ARRAY_SIZE] = kani::Arbitrary::any_array();
let test_ptr: *const $ty = arr.as_ptr();
let offset: usize = kani::any();
let count: usize = kani::any();
kani::assume(offset <= ARRAY_SIZE * mem::size_of::<$ty>());
let ptr_with_offset: *const $ty = test_ptr.wrapping_byte_add(offset);
unsafe {
ptr_with_offset.add(count);
}
}

// Generates a harness for the `sub` operation
#[kani::proof_for_contract(<*const $ty>::sub)]
fn $sub_fn() {
let arr: [$ty; ARRAY_SIZE] = kani::Arbitrary::any_array();
let test_ptr: *const $ty = arr.as_ptr();
let offset: usize = kani::any();
let count: usize = kani::any();
kani::assume(offset <= ARRAY_SIZE * mem::size_of::<$ty>());
let ptr_with_offset: *const $ty = test_ptr.wrapping_byte_add(offset);
unsafe {
ptr_with_offset.sub(count);
}
}
};
}

// Generate slice harnesses for various types (offset, add, sub)
generate_slice_harnesses!(i8, check_const_offset_slice_i8, check_const_add_slice_i8, check_const_sub_slice_i8);
generate_slice_harnesses!(i16, check_const_offset_slice_i16, check_const_add_slice_i16, check_const_sub_slice_i16);
generate_slice_harnesses!(i32, check_const_offset_slice_i32, check_const_add_slice_i32, check_const_sub_slice_i32);
generate_slice_harnesses!(i64, check_const_offset_slice_i64, check_const_add_slice_i64, check_const_sub_slice_i64);
generate_slice_harnesses!(i128, check_const_offset_slice_i128, check_const_add_slice_i128, check_const_sub_slice_i128);
generate_slice_harnesses!(isize, check_const_offset_slice_isize, check_const_add_slice_isize, check_const_sub_slice_isize);
generate_slice_harnesses!(u8, check_const_offset_slice_u8, check_const_add_slice_u8, check_const_sub_slice_u8);
generate_slice_harnesses!(u16, check_const_offset_slice_u16, check_const_add_slice_u16, check_const_sub_slice_u16);
generate_slice_harnesses!(u32, check_const_offset_slice_u32, check_const_add_slice_u32, check_const_sub_slice_u32);
generate_slice_harnesses!(u64, check_const_offset_slice_u64, check_const_add_slice_u64, check_const_sub_slice_u64);
generate_slice_harnesses!(u128, check_const_offset_slice_u128, check_const_add_slice_u128, check_const_sub_slice_u128);
generate_slice_harnesses!(usize, check_const_offset_slice_usize, check_const_add_slice_usize, check_const_sub_slice_usize);

// Generate slice harnesses for tuples (offset, add, sub)
tautschnig marked this conversation as resolved.
Show resolved Hide resolved
generate_slice_harnesses!(
(i8, i8),
check_const_offset_slice_tuple_1,
check_const_add_slice_tuple_1,
check_const_sub_slice_tuple_1
);
generate_slice_harnesses!(
(f64, bool),
check_const_offset_slice_tuple_2,
check_const_add_slice_tuple_2,
check_const_sub_slice_tuple_2
);
generate_slice_harnesses!(
(i32, f64, bool),
check_const_offset_slice_tuple_3,
check_const_add_slice_tuple_3,
check_const_sub_slice_tuple_3
);
generate_slice_harnesses!(
(i8, u16, i32, u64, isize),
check_const_offset_slice_tuple_4,
check_const_add_slice_tuple_4,
check_const_sub_slice_tuple_4
);

/// This macro generates proofs for contracts on `add`, `sub`, and `offset`
/// operations for pointers to integer, composite, and unit types.
/// - `$type`: Specifies the pointee type.
/// - `$proof_name`: Specifies the name of the generated proof for contract.
macro_rules! generate_const_arithmetic_harness {
($type:ty, $proof_name:ident, add) => {
#[kani::proof_for_contract(<*const $type>::add)]
pub fn $proof_name() {
// 200 bytes are large enough to cover all pointee types used for testing
const BUF_SIZE: usize = 200;
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
let test_ptr: *const $type = generator.any_in_bounds().ptr;
let count: usize = kani::any();
unsafe {
test_ptr.add(count);
}
}
};
($type:ty, $proof_name:ident, sub) => {
#[kani::proof_for_contract(<*const $type>::sub)]
pub fn $proof_name() {
// 200 bytes are large enough to cover all pointee types used for testing
const BUF_SIZE: usize = 200;
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
let test_ptr: *const $type = generator.any_in_bounds().ptr;
let count: usize = kani::any();
unsafe {
test_ptr.sub(count);
}
}
};
($type:ty, $proof_name:ident, offset) => {
#[kani::proof_for_contract(<*const $type>::offset)]
pub fn $proof_name() {
// 200 bytes are large enough to cover all pointee types used for testing
const BUF_SIZE: usize = 200;
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
let test_ptr: *const $type = generator.any_in_bounds().ptr;
let count: isize = kani::any();
unsafe {
test_ptr.offset(count);
}
}
};
}
feliperodri marked this conversation as resolved.
Show resolved Hide resolved

// <*const T>:: add() integer types verification
generate_const_arithmetic_harness!(i8, check_const_add_i8, add);
generate_const_arithmetic_harness!(i16, check_const_add_i16, add);
generate_const_arithmetic_harness!(i32, check_const_add_i32, add);
generate_const_arithmetic_harness!(i64, check_const_add_i64, add);
generate_const_arithmetic_harness!(i128, check_const_add_i128, add);
generate_const_arithmetic_harness!(isize, check_const_add_isize, add);
generate_const_arithmetic_harness!(u8, check_const_add_u8, add);
generate_const_arithmetic_harness!(u16, check_const_add_u16, add);
generate_const_arithmetic_harness!(u32, check_const_add_u32, add);
generate_const_arithmetic_harness!(u64, check_const_add_u64, add);
generate_const_arithmetic_harness!(u128, check_const_add_u128, add);
generate_const_arithmetic_harness!(usize, check_const_add_usize, add);

// <*const T>:: add() unit type verification
generate_const_arithmetic_harness!((), check_const_add_unit, add);

// <*const T>:: add() composite types verification
generate_const_arithmetic_harness!((i8, i8), check_const_add_tuple_1, add);
generate_const_arithmetic_harness!((f64, bool), check_const_add_tuple_2, add);
generate_const_arithmetic_harness!((i32, f64, bool), check_const_add_tuple_3, add);
generate_const_arithmetic_harness!((i8, u16, i32, u64, isize), check_const_add_tuple_4, add);

// <*const T>:: sub() integer types verification
generate_const_arithmetic_harness!(i8, check_const_sub_i8, sub);
generate_const_arithmetic_harness!(i16, check_const_sub_i16, sub);
generate_const_arithmetic_harness!(i32, check_const_sub_i32, sub);
generate_const_arithmetic_harness!(i64, check_const_sub_i64, sub);
generate_const_arithmetic_harness!(i128, check_const_sub_i128, sub);
generate_const_arithmetic_harness!(isize, check_const_sub_isize, sub);
generate_const_arithmetic_harness!(u8, check_const_sub_u8, sub);
generate_const_arithmetic_harness!(u16, check_const_sub_u16, sub);
generate_const_arithmetic_harness!(u32, check_const_sub_u32, sub);
generate_const_arithmetic_harness!(u64, check_const_sub_u64, sub);
generate_const_arithmetic_harness!(u128, check_const_sub_u128, sub);
generate_const_arithmetic_harness!(usize, check_const_sub_usize, sub);

// <*const T>:: sub() unit type verification
generate_const_arithmetic_harness!((), check_const_sub_unit, sub);

// <*const T>:: sub() composite types verification
generate_const_arithmetic_harness!((i8, i8), check_const_sub_tuple_1, sub);
generate_const_arithmetic_harness!((f64, bool), check_const_sub_tuple_2, sub);
generate_const_arithmetic_harness!((i32, f64, bool), check_const_sub_tuple_3, sub);
generate_const_arithmetic_harness!((i8, u16, i32, u64, isize), check_const_sub_tuple_4, sub);

// fn <*const T>::offset() integer types verification
generate_const_arithmetic_harness!(i8, check_const_offset_i8, offset);
generate_const_arithmetic_harness!(i16, check_const_offset_i16, offset);
generate_const_arithmetic_harness!(i32, check_const_offset_i32, offset);
generate_const_arithmetic_harness!(i64, check_const_offset_i64, offset);
generate_const_arithmetic_harness!(i128, check_const_offset_i128, offset);
generate_const_arithmetic_harness!(isize, check_const_offset_isize, offset);
generate_const_arithmetic_harness!(u8, check_const_offset_u8, offset);
generate_const_arithmetic_harness!(u16, check_const_offset_u16, offset);
generate_const_arithmetic_harness!(u32, check_const_offset_u32, offset);
generate_const_arithmetic_harness!(u64, check_const_offset_u64, offset);
generate_const_arithmetic_harness!(u128, check_const_offset_u128, offset);
generate_const_arithmetic_harness!(usize, check_const_offset_usize, offset);

// fn <*const T>::offset() unit type verification
generate_const_arithmetic_harness!((), check_const_offset_unit, offset);

// fn <*const T>::offset() composite type verification
generate_const_arithmetic_harness!((i8, i8), check_const_offset_tuple_1, offset);
generate_const_arithmetic_harness!((f64, bool), check_const_offset_tuple_2, offset);
generate_const_arithmetic_harness!((i32, f64, bool), check_const_offset_tuple_3, offset);
generate_const_arithmetic_harness!((i8, u16, i32, u64, isize), check_const_offset_tuple_4, offset);
}
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
Loading