Skip to content

Commit 014965a

Browse files
stogaruxsxszabMayureshJoshi25
authored
Contracts and Harnesses for <*mut T>::add, sub and offset (#113)
Towards #76 ### Changes * Adds contracts for `<*mut T>::add`, `<*mut T>::sub` and `<*mut T>::offset` * Adds proofs for contracts of the above functions verifying the pointee types: * All integer types * Tuples (composite type) * Unit Type * Defines a macro for add and sub and another for offset. 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: Yifei Wang <[email protected]> Co-authored-by: MayureshJoshi25 <[email protected]> Co-authored-by: Yifei Wang <[email protected]>
1 parent 716c6af commit 014965a

File tree

1 file changed

+177
-0
lines changed

1 file changed

+177
-0
lines changed

library/core/src/ptr/mut_ptr.rs

+177
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ use crate::cmp::Ordering::{Equal, Greater, Less};
33
use crate::intrinsics::const_eval_select;
44
use crate::mem::SizedTypeProperties;
55
use crate::slice::{self, SliceIndex};
6+
use safety::{ensures, requires};
7+
8+
#[cfg(kani)]
9+
use crate::kani;
610

711
impl<T: ?Sized> *mut T {
812
/// Returns `true` if the pointer is null.
@@ -400,6 +404,22 @@ impl<T: ?Sized> *mut T {
400404
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
401405
#[inline(always)]
402406
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
407+
// Note: It is the caller's responsibility to ensure that `self` is non-null and properly aligned.
408+
// These conditions are not verified as part of the preconditions.
409+
#[requires(
410+
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
411+
count.checked_mul(core::mem::size_of::<T>() as isize).is_some() &&
412+
// Precondition 2: adding the computed offset to `self` does not cause overflow
413+
(self as isize).checked_add((count * core::mem::size_of::<T>() as isize)).is_some() &&
414+
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
415+
// Otherwise, for non-unit types, `self` and `self.wrapping_offset(count)` should point to the same allocated object,
416+
// restricting `count` to prevent crossing allocation boundaries.
417+
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_offset(count))))
418+
)]
419+
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
420+
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
421+
// verifying that the result remains within the same allocation as `self`.
422+
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self as *const T, *result as *const T))]
403423
pub const unsafe fn offset(self, count: isize) -> *mut T
404424
where
405425
T: Sized,
@@ -998,6 +1018,23 @@ impl<T: ?Sized> *mut T {
9981018
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
9991019
#[inline(always)]
10001020
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1021+
// Note: It is the caller's responsibility to ensure that `self` is non-null and properly aligned.
1022+
// These conditions are not verified as part of the preconditions.
1023+
#[requires(
1024+
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
1025+
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
1026+
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
1027+
// Precondition 2: adding the computed offset to `self` does not cause overflow
1028+
(self as isize).checked_add((count * core::mem::size_of::<T>()) as isize).is_some() &&
1029+
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
1030+
// Otherwise, for non-unit types, `self` and `self.wrapping_add(count)` should point to the same allocated object,
1031+
// restricting `count` to prevent crossing allocation boundaries.
1032+
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_add(count))))
1033+
)]
1034+
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
1035+
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
1036+
// verifying that the result remains within the same allocation as `self`.
1037+
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self as *const T, *result as *const T))]
10011038
pub const unsafe fn add(self, count: usize) -> Self
10021039
where
10031040
T: Sized,
@@ -1107,6 +1144,23 @@ impl<T: ?Sized> *mut T {
11071144
#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))]
11081145
#[inline(always)]
11091146
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1147+
// Note: It is the caller's responsibility to ensure that `self` is non-null and properly aligned.
1148+
// These conditions are not verified as part of the preconditions.
1149+
#[requires(
1150+
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
1151+
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
1152+
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
1153+
// Precondition 2: subtracting the computed offset from `self` does not cause overflow
1154+
(self as isize).checked_sub((count * core::mem::size_of::<T>()) as isize).is_some() &&
1155+
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
1156+
// Otherwise, for non-unit types, `self` and `self.wrapping_sub(count)` should point to the same allocated object,
1157+
// restricting `count` to prevent crossing allocation boundaries.
1158+
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_sub(count))))
1159+
)]
1160+
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
1161+
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
1162+
// verifying that the result remains within the same allocation as `self`.
1163+
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self as *const T, *result as *const T))]
11101164
pub const unsafe fn sub(self, count: usize) -> Self
11111165
where
11121166
T: Sized,
@@ -2302,3 +2356,126 @@ impl<T: ?Sized> PartialOrd for *mut T {
23022356
*self >= *other
23032357
}
23042358
}
2359+
2360+
#[cfg(kani)]
2361+
#[unstable(feature = "kani", issue = "none")]
2362+
mod verify {
2363+
use crate::kani;
2364+
2365+
/// This macro generates proofs for contracts on `add`, `sub`, and `offset`
2366+
/// operations for pointers to integer, composite, and unit types.
2367+
/// - `$type`: Specifies the pointee type.
2368+
/// - `$proof_name`: Specifies the name of the generated proof for contract.
2369+
macro_rules! generate_mut_arithmetic_harness {
2370+
($type:ty, $proof_name:ident, add) => {
2371+
#[kani::proof_for_contract(<*mut $type>::add)]
2372+
pub fn $proof_name() {
2373+
// 200 bytes are large enough to cover all pointee types used for testing
2374+
const BUF_SIZE: usize = 200;
2375+
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
2376+
let test_ptr: *mut $type = generator.any_in_bounds().ptr;
2377+
let count: usize = kani::any();
2378+
unsafe {
2379+
test_ptr.add(count);
2380+
}
2381+
}
2382+
};
2383+
($type:ty, $proof_name:ident, sub) => {
2384+
#[kani::proof_for_contract(<*mut $type>::sub)]
2385+
pub fn $proof_name() {
2386+
// 200 bytes are large enough to cover all pointee types used for testing
2387+
const BUF_SIZE: usize = 200;
2388+
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
2389+
let test_ptr: *mut $type = generator.any_in_bounds().ptr;
2390+
let count: usize = kani::any();
2391+
unsafe {
2392+
test_ptr.sub(count);
2393+
}
2394+
}
2395+
};
2396+
($type:ty, $proof_name:ident, offset) => {
2397+
#[kani::proof_for_contract(<*mut $type>::offset)]
2398+
pub fn $proof_name() {
2399+
// 200 bytes are large enough to cover all pointee types used for testing
2400+
const BUF_SIZE: usize = 200;
2401+
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
2402+
let test_ptr: *mut $type = generator.any_in_bounds().ptr;
2403+
let count: isize = kani::any();
2404+
unsafe {
2405+
test_ptr.offset(count);
2406+
}
2407+
}
2408+
};
2409+
}
2410+
2411+
// <*mut T>:: add() integer types verification
2412+
generate_mut_arithmetic_harness!(i8, check_mut_add_i8, add);
2413+
generate_mut_arithmetic_harness!(i16, check_mut_add_i16, add);
2414+
generate_mut_arithmetic_harness!(i32, check_mut_add_i32, add);
2415+
generate_mut_arithmetic_harness!(i64, check_mut_add_i64, add);
2416+
generate_mut_arithmetic_harness!(i128, check_mut_add_i128, add);
2417+
generate_mut_arithmetic_harness!(isize, check_mut_add_isize, add);
2418+
// Due to a bug of kani this test case is malfunctioning for now.
2419+
// Tracking issue: https://github.com/model-checking/kani/issues/3743
2420+
// generate_mut_arithmetic_harness!(u8, check_mut_add_u8, add);
2421+
generate_mut_arithmetic_harness!(u16, check_mut_add_u16, add);
2422+
generate_mut_arithmetic_harness!(u32, check_mut_add_u32, add);
2423+
generate_mut_arithmetic_harness!(u64, check_mut_add_u64, add);
2424+
generate_mut_arithmetic_harness!(u128, check_mut_add_u128, add);
2425+
generate_mut_arithmetic_harness!(usize, check_mut_add_usize, add);
2426+
2427+
// <*mut T>:: add() unit type verification
2428+
generate_mut_arithmetic_harness!((), check_mut_add_unit, add);
2429+
2430+
// <*mut T>:: add() composite types verification
2431+
generate_mut_arithmetic_harness!((i8, i8), check_mut_add_tuple_1, add);
2432+
generate_mut_arithmetic_harness!((f64, bool), check_mut_add_tuple_2, add);
2433+
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_add_tuple_3, add);
2434+
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_add_tuple_4, add);
2435+
2436+
// <*mut T>:: sub() integer types verification
2437+
generate_mut_arithmetic_harness!(i8, check_mut_sub_i8, sub);
2438+
generate_mut_arithmetic_harness!(i16, check_mut_sub_i16, sub);
2439+
generate_mut_arithmetic_harness!(i32, check_mut_sub_i32, sub);
2440+
generate_mut_arithmetic_harness!(i64, check_mut_sub_i64, sub);
2441+
generate_mut_arithmetic_harness!(i128, check_mut_sub_i128, sub);
2442+
generate_mut_arithmetic_harness!(isize, check_mut_sub_isize, sub);
2443+
generate_mut_arithmetic_harness!(u8, check_mut_sub_u8, sub);
2444+
generate_mut_arithmetic_harness!(u16, check_mut_sub_u16, sub);
2445+
generate_mut_arithmetic_harness!(u32, check_mut_sub_u32, sub);
2446+
generate_mut_arithmetic_harness!(u64, check_mut_sub_u64, sub);
2447+
generate_mut_arithmetic_harness!(u128, check_mut_sub_u128, sub);
2448+
generate_mut_arithmetic_harness!(usize, check_mut_sub_usize, sub);
2449+
2450+
// <*mut T>:: sub() unit type verification
2451+
generate_mut_arithmetic_harness!((), check_mut_sub_unit, sub);
2452+
2453+
// <*mut T>:: sub() composite types verification
2454+
generate_mut_arithmetic_harness!((i8, i8), check_mut_sub_tuple_1, sub);
2455+
generate_mut_arithmetic_harness!((f64, bool), check_mut_sub_tuple_2, sub);
2456+
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_sub_tuple_3, sub);
2457+
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_sub_tuple_4, sub);
2458+
2459+
// fn <*mut T>::offset() integer types verification
2460+
generate_mut_arithmetic_harness!(i8, check_mut_offset_i8, offset);
2461+
generate_mut_arithmetic_harness!(i16, check_mut_offset_i16, offset);
2462+
generate_mut_arithmetic_harness!(i32, check_mut_offset_i32, offset);
2463+
generate_mut_arithmetic_harness!(i64, check_mut_offset_i64, offset);
2464+
generate_mut_arithmetic_harness!(i128, check_mut_offset_i128, offset);
2465+
generate_mut_arithmetic_harness!(isize, check_mut_offset_isize, offset);
2466+
generate_mut_arithmetic_harness!(u8, check_mut_offset_u8, offset);
2467+
generate_mut_arithmetic_harness!(u16, check_mut_offset_u16, offset);
2468+
generate_mut_arithmetic_harness!(u32, check_mut_offset_u32, offset);
2469+
generate_mut_arithmetic_harness!(u64, check_mut_offset_u64, offset);
2470+
generate_mut_arithmetic_harness!(u128, check_mut_offset_u128, offset);
2471+
generate_mut_arithmetic_harness!(usize, check_mut_offset_usize, offset);
2472+
2473+
// fn <*mut T>::offset() unit type verification
2474+
generate_mut_arithmetic_harness!((), check_mut_offset_unit, offset);
2475+
2476+
// fn <*mut T>::offset() composite type verification
2477+
generate_mut_arithmetic_harness!((i8, i8), check_mut_offset_tuple_1, offset);
2478+
generate_mut_arithmetic_harness!((f64, bool), check_mut_offset_tuple_2, offset);
2479+
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_offset_tuple_3, offset);
2480+
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_offset_tuple_4, offset);
2481+
}

0 commit comments

Comments
 (0)