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

Added <dyn Trait> Proof for Contracts for byte_add, byte_sub, and byte_offset #188

Merged
merged 33 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0ccf81e
Adds contracts for byte_add, byte_sub and byte_offset
stogaru Nov 14, 2024
aec0394
Removes unnecessary rustfmt formnatting
stogaru Nov 15, 2024
60ba93e
Adds comment for amgic numbers
stogaru Nov 15, 2024
57c591f
Adds some comments
stogaru Nov 15, 2024
a02ed87
Adds slice harnesses
stogaru Nov 15, 2024
2fdebd6
Adds contracts for byte_offset, byte_add and byte_sub
stogaru Nov 16, 2024
cd0a134
Runs rustfmt
stogaru Nov 16, 2024
4aa89da
Merge branch 'main' into verify/ptr_mut_byte
stogaru Nov 19, 2024
fdfad2c
Merge branch 'main' into verify/ptr_mut_byte
stogaru Nov 21, 2024
40eb29d
Some refactoring
stogaru Nov 21, 2024
81fe7c9
Refactors the function contracts
stogaru Nov 21, 2024
bee4503
Merge branch 'main' into verify/ptr_const_byte
stogaru Nov 22, 2024
52963f7
Merge branch 'main' into verify/ptr_mut_byte
stogaru Nov 24, 2024
38066e1
Merge branch 'main' into verify/ptr_const_byte
stogaru Nov 24, 2024
2d32b07
Merge branch 'verify/ptr_const_byte' into verify/ptr_mut_byte
stogaru Nov 24, 2024
5e4f616
Addresses commennts on PR
stogaru Nov 26, 2024
dea6e0e
Merge branch 'main' into verify/ptr_mut_byte
stogaru Nov 26, 2024
7e979cb
Implemented proof for contracts for dyn trait for byte_add, byte_sub …
xsxszab Nov 27, 2024
c98d839
Reverted unnecessary changes to minimize conflict.
xsxszab Nov 27, 2024
5cffb98
Merge branch 'main' into verify/ptr_byte_dyn
xsxszab Nov 28, 2024
1dab4e3
Formatted code using rustfmt
xsxszab Nov 28, 2024
572f092
Merge branch 'main' into verify/ptr_byte_dyn
xsxszab Dec 4, 2024
df6d4ed
Manually updated changes from verify/ptr_mut_byte.
xsxszab Dec 4, 2024
633df86
Updated tracking issues; Updated magic number comments; Replaced kani…
xsxszab Dec 5, 2024
5069cf3
Formatted code using rustfmt.
xsxszab Dec 5, 2024
8736e19
Merge branch 'main' into verify/ptr_byte_dyn
xsxszab Dec 6, 2024
d5ab23f
Rearranged proof for contracts.
xsxszab Dec 6, 2024
f81a009
Improved comments for dyn Trait related code.
xsxszab Dec 6, 2024
7d2e9ad
Merge branch 'main' into verify/ptr_byte_dyn
feliperodri Dec 7, 2024
e360f06
Merge branch 'main' into verify/ptr_byte_dyn
xsxszab Dec 9, 2024
7b61c2f
Fixed compilation error after merge.
xsxszab Dec 9, 2024
b03ba84
Rearranged Proofs in const_ptr.rs; Formatted code using rustfmt.
xsxszab Dec 9, 2024
72c922b
Improved comments.
xsxszab Dec 9, 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
101 changes: 100 additions & 1 deletion library/core/src/ptr/const_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use safety::{ensures, requires};

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

impl<T: ?Sized> *const T {
/// Returns `true` if the pointer is null.
Expand Down Expand Up @@ -463,6 +464,21 @@ impl<T: ?Sized> *const T {
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// If count is zero, any pointer is valid including null pointer.
(count == 0) ||
// Else if count is not zero, then ensure that adding `count` doesn't cause
// overflow and that both pointers `self` and the result are in the same
// allocation
(mem::size_of_val_raw(self) != 0 &&
(self.addr() as isize).checked_add(count).is_some() &&
kani::mem::same_allocation(self, self.wrapping_byte_offset(count)))
)]
#[ensures(|result|
// The resulting pointer should either be unchanged or still point to the same allocation
(self.addr() == (*result).addr()) ||
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
(kani::mem::same_allocation(self, *result))
)]
pub const unsafe fn byte_offset(self, count: isize) -> Self {
// SAFETY: the caller must uphold the safety contract for `offset`.
unsafe { self.cast::<u8>().offset(count).with_metadata_of(self) }
Expand Down Expand Up @@ -986,6 +1002,21 @@ impl<T: ?Sized> *const T {
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// If count is zero, any pointer is valid including null pointer.
(count == 0) ||
// Else if count is not zero, then ensure that adding `count` doesn't cause
// overflow and that both pointers `self` and the result are in the same
// allocation
(mem::size_of_val_raw(self) != 0 &&
(self.addr() as isize).checked_add(count as isize).is_some() &&
kani::mem::same_allocation(self, self.wrapping_byte_add(count)))
)]
#[ensures(|result|
// The resulting pointer should either be unchanged or still point to the same allocation
(self.addr() == (*result).addr()) ||
feliperodri marked this conversation as resolved.
Show resolved Hide resolved
(kani::mem::same_allocation(self, *result))
)]
pub const unsafe fn byte_add(self, count: usize) -> Self {
// SAFETY: the caller must uphold the safety contract for `add`.
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
Expand Down Expand Up @@ -1101,6 +1132,21 @@ impl<T: ?Sized> *const T {
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// If count is zero, any pointer is valid including null pointer.
(count == 0) ||
// Else if count is not zero, then ensure that subtracting `count` doesn't
// cause overflow and that both pointers `self` and the result are in the
// same allocation
(mem::size_of_val_raw(self) != 0 &&
(self.addr() as isize).checked_sub(count as isize).is_some() &&
kani::mem::same_allocation(self, self.wrapping_byte_sub(count)))
)]
#[ensures(|result|
// The resulting pointer should either be unchanged or still point to the same allocation
(self.addr() == (*result).addr()) ||
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
(kani::mem::same_allocation(self, *result))
)]
pub const unsafe fn byte_sub(self, count: usize) -> Self {
// SAFETY: the caller must uphold the safety contract for `sub`.
unsafe { self.cast::<u8>().sub(count).with_metadata_of(self) }
Expand Down Expand Up @@ -1916,7 +1962,7 @@ impl<T: ?Sized> PartialOrd for *const T {

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
pub mod verify {
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
use crate::kani;
use core::mem;
use kani::PointerGenerator;
Expand Down Expand Up @@ -2058,4 +2104,57 @@ mod verify {
check_const_offset_from_tuple_4,
check_const_offset_from_tuple_4_arr
);

trait TestTrait {}

struct TestStruct {
value: i64,
}

impl TestTrait for TestStruct {}

macro_rules! gen_const_byte_arith_harness_for_dyn {
(byte_offset, $proof_name:ident) => {
// Workaround: Directly verifying the method `<*const dyn TestTrait>::byte_offset`
// causes a compilation error: "Failed to resolve checking function <*const dyn TestTrait>::byte_offset
// because Expected a type, but found trait object paths `dyn TestTrait`".
// As a result, the proof is annotated for the underlying struct type instead.
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
#[kani::proof_for_contract(<*const TestStruct>::byte_offset)]
pub fn $proof_name() {
let test_struct = TestStruct { value: 42 };
let trait_object: &dyn TestTrait = &test_struct;
let test_ptr: *const dyn TestTrait = trait_object;

let count: isize = kani::any();

unsafe {
test_ptr.byte_offset(count);
}
}
};

($fn_name: ident, $proof_name:ident) => {
// Workaround: Directly verifying the method `<*const dyn TestTrait>::$fn_name`
// causes a compilation error: "Failed to resolve checking function <*const dyn TestTrait>::byte_offset
// because Expected a type, but found trait object paths `dyn TestTrait`".
// As a result, the proof is annotated for the underlying struct type instead.
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
#[kani::proof_for_contract(<*const TestStruct>::$fn_name)]
pub fn $proof_name() {
let test_struct = TestStruct { value: 42 };
let trait_object: &dyn TestTrait = &test_struct;
let test_ptr: *const dyn TestTrait = trait_object;

//byte_add and byte_sub need count to be usize unlike byte_offset
let count: usize = kani::any();

unsafe {
test_ptr.$fn_name(count);
}
}
};
}

gen_const_byte_arith_harness_for_dyn!(byte_add, check_const_byte_add_dyn);
gen_const_byte_arith_harness_for_dyn!(byte_sub, check_const_byte_sub_dyn);
gen_const_byte_arith_harness_for_dyn!(byte_offset, check_const_byte_offset_dyn);
}
107 changes: 106 additions & 1 deletion library/core/src/ptr/mut_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use safety::{ensures, requires};

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

impl<T: ?Sized> *mut T {
/// Returns `true` if the pointer is null.
Expand Down Expand Up @@ -477,6 +478,21 @@ impl<T: ?Sized> *mut T {
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// If count is zero, any pointer is valid including null pointer.
(count == 0) ||
// Else if count is not zero, then ensure that subtracting `count` doesn't
// cause overflow and that both pointers `self` and the result are in the
// same allocation
(mem::size_of_val_raw(self) != 0 &&
(self.addr() as isize).checked_add(count).is_some() &&
kani::mem::same_allocation(self as *const T, self.wrapping_byte_offset(count) as *const T))
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
)]
#[ensures(|result|
// The resulting pointer should either be unchanged or still point to the same allocation
(self.addr() == (*result).addr()) ||
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
(kani::mem::same_allocation(self as *const T, *result as *const T))
)]
pub const unsafe fn byte_offset(self, count: isize) -> Self {
// SAFETY: the caller must uphold the safety contract for `offset`.
unsafe { self.cast::<u8>().offset(count).with_metadata_of(self) }
Expand Down Expand Up @@ -1089,6 +1105,21 @@ impl<T: ?Sized> *mut T {
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// If count is zero, any pointer is valid including null pointer.
(count == 0) ||
// Else if count is not zero, then ensure that subtracting `count` doesn't
// cause overflow and that both pointers `self` and the result are in the
// same allocation
(mem::size_of_val_raw(self) != 0 &&
(self.addr() as isize).checked_add(count as isize).is_some() &&
kani::mem::same_allocation(self as *const T, self.wrapping_byte_add(count) as *const T))
)]
#[ensures(|result|
// The resulting pointer should either be unchanged or still point to the same allocation
(self.addr() == (*result).addr()) ||
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
(kani::mem::same_allocation(self as *const T, *result as *const T))
)]
pub const unsafe fn byte_add(self, count: usize) -> Self {
// SAFETY: the caller must uphold the safety contract for `add`.
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
Expand Down Expand Up @@ -1221,6 +1252,21 @@ impl<T: ?Sized> *mut T {
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
#[requires(
// If count is zero, any pointer is valid including null pointer.
(count == 0) ||
// Else if count is not zero, then ensure that subtracting `count` doesn't
// cause overflow and that both pointers `self` and the result are in the
// same allocation
(mem::size_of_val_raw(self) != 0 &&
(self.addr() as isize).checked_sub(count as isize).is_some() &&
kani::mem::same_allocation(self as *const T, self.wrapping_byte_sub(count) as *const T))
)]
#[ensures(|result|
// The resulting pointer should either be unchanged or still point to the same allocation
(self.addr() == (*result).addr()) ||
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
(kani::mem::same_allocation(self as *const T, *result as *const T))
)]
pub const unsafe fn byte_sub(self, count: usize) -> Self {
// SAFETY: the caller must uphold the safety contract for `sub`.
unsafe { self.cast::<u8>().sub(count).with_metadata_of(self) }
Expand Down Expand Up @@ -2359,8 +2405,10 @@ impl<T: ?Sized> PartialOrd for *mut T {

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
pub mod verify {
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
use crate::kani;
use core::mem;
use kani::PointerGenerator;

/// This macro generates proofs for contracts on `add`, `sub`, and `offset`
/// operations for pointers to integer, composite, and unit types.
Expand Down Expand Up @@ -2478,4 +2526,61 @@ mod verify {
generate_mut_arithmetic_harness!((f64, bool), check_mut_offset_tuple_2, offset);
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_offset_tuple_3, offset);
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_offset_tuple_4, offset);

trait TestTrait {}

struct TestStruct {
value: i64,
}

impl TestTrait for TestStruct {}

/// This macro generates proofs for contracts on `byte_add`, `byte_sub`, and `byte_offset`
/// operations for pointers to dyn Trait.
/// - `$fn_name`: Specifies the arithmetic operation to verify.
/// - `$proof_name`: Specifies the name of the generated proof for contract.
macro_rules! gen_mut_byte_arith_harness_for_dyn {
(byte_offset, $proof_name:ident) => {
// Workaround: Directly verifying the method `<*mut dyn TestTrait>::byte_offset`
// causes a compilation error: "Failed to resolve checking function <*mut dyn TestTrait>::byte_offset
// because Expected a type, but found trait object paths `dyn TestTrait`".
// As a result, the proof is annotated for the underlying struct type instead.
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
#[kani::proof_for_contract(<*mut TestStruct>::byte_offset)]
pub fn $proof_name() {
let mut test_struct = TestStruct { value: 42 };
let trait_object: &mut dyn TestTrait = &mut test_struct;
let test_ptr: *mut dyn TestTrait = trait_object;

let count: isize = kani::any();

unsafe {
test_ptr.byte_offset(count);
}
}
};
($fn_name: ident, $proof_name:ident) => {
// Workaround: Directly verifying the method `<*mut dyn TestTrait>::$fn_name`
// causes a compilation error: "Failed to resolve checking function <*mut dyn TestTrait>::byte_offset
// because Expected a type, but found trait object paths `dyn TestTrait`".
// As a result, the proof is annotated for the underlying struct type instead.
xsxszab marked this conversation as resolved.
Show resolved Hide resolved
#[kani::proof_for_contract(<*mut TestStruct>::$fn_name)]
pub fn $proof_name() {
let mut test_struct = TestStruct { value: 42 };
let trait_object: &mut dyn TestTrait = &mut test_struct;
let test_ptr: *mut dyn TestTrait = trait_object;

//byte_add and byte_sub need count to be usize unlike byte_offset
let count: usize = kani::any();

unsafe {
test_ptr.$fn_name(count);
}
}
};
}

// fn <*mut T>::add(), <*mut T>::sub() and <*mut T>::offset() dyn Trait verification
gen_mut_byte_arith_harness_for_dyn!(byte_add, check_mut_byte_add_dyn);
gen_mut_byte_arith_harness_for_dyn!(byte_sub, check_mut_byte_sub_dyn);
gen_mut_byte_arith_harness_for_dyn!(byte_offset, check_mut_byte_offset_dyn);
}
Loading