Skip to content

Commit 1d0fb75

Browse files
authored
Merge branch 'main' into verify/ptr_const_byte_offset_from
2 parents 4251118 + 014965a commit 1d0fb75

File tree

3 files changed

+257
-2
lines changed

3 files changed

+257
-2
lines changed

.github/pull_requests.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ members = [
1616
"robdockins",
1717
"HuStmpHrrr",
1818
"Eh2406",
19-
"jswrenn"
19+
"jswrenn",
20+
"havelund",
21+
"jorajeev"
2022
]

library/core/src/ptr/mut_ptr.rs

+167-1
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,22 @@ impl<T: ?Sized> *mut T {
405405
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
406406
#[inline(always)]
407407
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
408+
// Note: It is the caller's responsibility to ensure that `self` is non-null and properly aligned.
409+
// These conditions are not verified as part of the preconditions.
410+
#[requires(
411+
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
412+
count.checked_mul(core::mem::size_of::<T>() as isize).is_some() &&
413+
// Precondition 2: adding the computed offset to `self` does not cause overflow
414+
(self as isize).checked_add((count * core::mem::size_of::<T>() as isize)).is_some() &&
415+
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
416+
// Otherwise, for non-unit types, `self` and `self.wrapping_offset(count)` should point to the same allocated object,
417+
// restricting `count` to prevent crossing allocation boundaries.
418+
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_offset(count))))
419+
)]
420+
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
421+
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
422+
// verifying that the result remains within the same allocation as `self`.
423+
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self as *const T, *result as *const T))]
408424
pub const unsafe fn offset(self, count: isize) -> *mut T
409425
where
410426
T: Sized,
@@ -1013,6 +1029,23 @@ impl<T: ?Sized> *mut T {
10131029
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
10141030
#[inline(always)]
10151031
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1032+
// Note: It is the caller's responsibility to ensure that `self` is non-null and properly aligned.
1033+
// These conditions are not verified as part of the preconditions.
1034+
#[requires(
1035+
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
1036+
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
1037+
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
1038+
// Precondition 2: adding the computed offset to `self` does not cause overflow
1039+
(self as isize).checked_add((count * core::mem::size_of::<T>()) as isize).is_some() &&
1040+
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
1041+
// Otherwise, for non-unit types, `self` and `self.wrapping_add(count)` should point to the same allocated object,
1042+
// restricting `count` to prevent crossing allocation boundaries.
1043+
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_add(count))))
1044+
)]
1045+
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
1046+
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
1047+
// verifying that the result remains within the same allocation as `self`.
1048+
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self as *const T, *result as *const T))]
10161049
pub const unsafe fn add(self, count: usize) -> Self
10171050
where
10181051
T: Sized,
@@ -1122,6 +1155,23 @@ impl<T: ?Sized> *mut T {
11221155
#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))]
11231156
#[inline(always)]
11241157
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1158+
// Note: It is the caller's responsibility to ensure that `self` is non-null and properly aligned.
1159+
// These conditions are not verified as part of the preconditions.
1160+
#[requires(
1161+
// Precondition 1: the computed offset `count * size_of::<T>()` does not overflow `isize`
1162+
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
1163+
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
1164+
// Precondition 2: subtracting the computed offset from `self` does not cause overflow
1165+
(self as isize).checked_sub((count * core::mem::size_of::<T>()) as isize).is_some() &&
1166+
// Precondition 3: If `T` is a unit type (`size_of::<T>() == 0`), this check is unnecessary as it has no allocated memory.
1167+
// Otherwise, for non-unit types, `self` and `self.wrapping_sub(count)` should point to the same allocated object,
1168+
// restricting `count` to prevent crossing allocation boundaries.
1169+
((core::mem::size_of::<T>() == 0) || (kani::mem::same_allocation(self, self.wrapping_sub(count))))
1170+
)]
1171+
// Postcondition: If `T` is a unit type (`size_of::<T>() == 0`), no allocation check is needed.
1172+
// Otherwise, for non-unit types, ensure that `self` and `result` point to the same allocated object,
1173+
// verifying that the result remains within the same allocation as `self`.
1174+
#[ensures(|result| (core::mem::size_of::<T>() == 0) || kani::mem::same_allocation(self as *const T, *result as *const T))]
11251175
pub const unsafe fn sub(self, count: usize) -> Self
11261176
where
11271177
T: Sized,
@@ -2506,5 +2556,121 @@ pub mod verify {
25062556
generate_mut_byte_offset_from_slice_harness!(i64, check_mut_byte_offset_from_i64_slice);
25072557
generate_mut_byte_offset_from_slice_harness!(i128, check_mut_byte_offset_from_i128_slice);
25082558
generate_mut_byte_offset_from_slice_harness!(isize, check_mut_byte_offset_from_isize_slice);
2509-
2559+
2560+
/// This macro generates proofs for contracts on `add`, `sub`, and `offset`
2561+
/// operations for pointers to integer, composite, and unit types.
2562+
/// - `$type`: Specifies the pointee type.
2563+
/// - `$proof_name`: Specifies the name of the generated proof for contract.
2564+
macro_rules! generate_mut_arithmetic_harness {
2565+
($type:ty, $proof_name:ident, add) => {
2566+
#[kani::proof_for_contract(<*mut $type>::add)]
2567+
pub fn $proof_name() {
2568+
// 200 bytes are large enough to cover all pointee types used for testing
2569+
const BUF_SIZE: usize = 200;
2570+
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
2571+
let test_ptr: *mut $type = generator.any_in_bounds().ptr;
2572+
let count: usize = kani::any();
2573+
unsafe {
2574+
test_ptr.add(count);
2575+
}
2576+
}
2577+
};
2578+
($type:ty, $proof_name:ident, sub) => {
2579+
#[kani::proof_for_contract(<*mut $type>::sub)]
2580+
pub fn $proof_name() {
2581+
// 200 bytes are large enough to cover all pointee types used for testing
2582+
const BUF_SIZE: usize = 200;
2583+
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
2584+
let test_ptr: *mut $type = generator.any_in_bounds().ptr;
2585+
let count: usize = kani::any();
2586+
unsafe {
2587+
test_ptr.sub(count);
2588+
}
2589+
}
2590+
};
2591+
($type:ty, $proof_name:ident, offset) => {
2592+
#[kani::proof_for_contract(<*mut $type>::offset)]
2593+
pub fn $proof_name() {
2594+
// 200 bytes are large enough to cover all pointee types used for testing
2595+
const BUF_SIZE: usize = 200;
2596+
let mut generator = kani::PointerGenerator::<BUF_SIZE>::new();
2597+
let test_ptr: *mut $type = generator.any_in_bounds().ptr;
2598+
let count: isize = kani::any();
2599+
unsafe {
2600+
test_ptr.offset(count);
2601+
}
2602+
}
2603+
};
2604+
}
2605+
2606+
// <*mut T>:: add() integer types verification
2607+
generate_mut_arithmetic_harness!(i8, check_mut_add_i8, add);
2608+
generate_mut_arithmetic_harness!(i16, check_mut_add_i16, add);
2609+
generate_mut_arithmetic_harness!(i32, check_mut_add_i32, add);
2610+
generate_mut_arithmetic_harness!(i64, check_mut_add_i64, add);
2611+
generate_mut_arithmetic_harness!(i128, check_mut_add_i128, add);
2612+
generate_mut_arithmetic_harness!(isize, check_mut_add_isize, add);
2613+
// Due to a bug of kani this test case is malfunctioning for now.
2614+
// Tracking issue: https://github.com/model-checking/kani/issues/3743
2615+
// generate_mut_arithmetic_harness!(u8, check_mut_add_u8, add);
2616+
generate_mut_arithmetic_harness!(u16, check_mut_add_u16, add);
2617+
generate_mut_arithmetic_harness!(u32, check_mut_add_u32, add);
2618+
generate_mut_arithmetic_harness!(u64, check_mut_add_u64, add);
2619+
generate_mut_arithmetic_harness!(u128, check_mut_add_u128, add);
2620+
generate_mut_arithmetic_harness!(usize, check_mut_add_usize, add);
2621+
2622+
// <*mut T>:: add() unit type verification
2623+
generate_mut_arithmetic_harness!((), check_mut_add_unit, add);
2624+
2625+
// <*mut T>:: add() composite types verification
2626+
generate_mut_arithmetic_harness!((i8, i8), check_mut_add_tuple_1, add);
2627+
generate_mut_arithmetic_harness!((f64, bool), check_mut_add_tuple_2, add);
2628+
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_add_tuple_3, add);
2629+
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_add_tuple_4, add);
2630+
2631+
// <*mut T>:: sub() integer types verification
2632+
generate_mut_arithmetic_harness!(i8, check_mut_sub_i8, sub);
2633+
generate_mut_arithmetic_harness!(i16, check_mut_sub_i16, sub);
2634+
generate_mut_arithmetic_harness!(i32, check_mut_sub_i32, sub);
2635+
generate_mut_arithmetic_harness!(i64, check_mut_sub_i64, sub);
2636+
generate_mut_arithmetic_harness!(i128, check_mut_sub_i128, sub);
2637+
generate_mut_arithmetic_harness!(isize, check_mut_sub_isize, sub);
2638+
generate_mut_arithmetic_harness!(u8, check_mut_sub_u8, sub);
2639+
generate_mut_arithmetic_harness!(u16, check_mut_sub_u16, sub);
2640+
generate_mut_arithmetic_harness!(u32, check_mut_sub_u32, sub);
2641+
generate_mut_arithmetic_harness!(u64, check_mut_sub_u64, sub);
2642+
generate_mut_arithmetic_harness!(u128, check_mut_sub_u128, sub);
2643+
generate_mut_arithmetic_harness!(usize, check_mut_sub_usize, sub);
2644+
2645+
// <*mut T>:: sub() unit type verification
2646+
generate_mut_arithmetic_harness!((), check_mut_sub_unit, sub);
2647+
2648+
// <*mut T>:: sub() composite types verification
2649+
generate_mut_arithmetic_harness!((i8, i8), check_mut_sub_tuple_1, sub);
2650+
generate_mut_arithmetic_harness!((f64, bool), check_mut_sub_tuple_2, sub);
2651+
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_sub_tuple_3, sub);
2652+
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_sub_tuple_4, sub);
2653+
2654+
// fn <*mut T>::offset() integer types verification
2655+
generate_mut_arithmetic_harness!(i8, check_mut_offset_i8, offset);
2656+
generate_mut_arithmetic_harness!(i16, check_mut_offset_i16, offset);
2657+
generate_mut_arithmetic_harness!(i32, check_mut_offset_i32, offset);
2658+
generate_mut_arithmetic_harness!(i64, check_mut_offset_i64, offset);
2659+
generate_mut_arithmetic_harness!(i128, check_mut_offset_i128, offset);
2660+
generate_mut_arithmetic_harness!(isize, check_mut_offset_isize, offset);
2661+
generate_mut_arithmetic_harness!(u8, check_mut_offset_u8, offset);
2662+
generate_mut_arithmetic_harness!(u16, check_mut_offset_u16, offset);
2663+
generate_mut_arithmetic_harness!(u32, check_mut_offset_u32, offset);
2664+
generate_mut_arithmetic_harness!(u64, check_mut_offset_u64, offset);
2665+
generate_mut_arithmetic_harness!(u128, check_mut_offset_u128, offset);
2666+
generate_mut_arithmetic_harness!(usize, check_mut_offset_usize, offset);
2667+
2668+
// fn <*mut T>::offset() unit type verification
2669+
generate_mut_arithmetic_harness!((), check_mut_offset_unit, offset);
2670+
2671+
// fn <*mut T>::offset() composite type verification
2672+
generate_mut_arithmetic_harness!((i8, i8), check_mut_offset_tuple_1, offset);
2673+
generate_mut_arithmetic_harness!((f64, bool), check_mut_offset_tuple_2, offset);
2674+
generate_mut_arithmetic_harness!((i32, f64, bool), check_mut_offset_tuple_3, offset);
2675+
generate_mut_arithmetic_harness!((i8, u16, i32, u64, isize), check_mut_offset_tuple_4, offset);
25102676
}

library/core/src/ptr/non_null.rs

+87
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,12 @@ impl<T: ?Sized> NonNull<T> {
665665
#[stable(feature = "non_null_convenience", since = "1.80.0")]
666666
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
667667
#[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(unchecked_neg))]
668+
#[requires(
669+
count.checked_mul(core::mem::size_of::<T>()).is_some() &&
670+
count * core::mem::size_of::<T>() <= isize::MAX as usize &&
671+
kani::mem::same_allocation(self.as_ptr(), self.as_ptr().wrapping_sub(count))
672+
)]
673+
#[ensures(|result: &NonNull<T>| result.as_ptr() == self.as_ptr().offset(-(count as isize)))]
668674
pub const unsafe fn sub(self, count: usize) -> Self
669675
where
670676
T: Sized,
@@ -794,6 +800,11 @@ impl<T: ?Sized> NonNull<T> {
794800
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
795801
#[stable(feature = "non_null_convenience", since = "1.80.0")]
796802
#[rustc_const_stable(feature = "non_null_convenience", since = "1.80.0")]
803+
#[requires(
804+
(kani::mem::same_allocation(self.as_ptr(), origin.as_ptr()) &&
805+
((self.as_ptr().addr() - origin.as_ptr().addr()) % core::mem::size_of::<T>() == 0))
806+
)] // Ensure both pointers meet safety conditions for offset_from
807+
#[ensures(|result: &isize| *result == (self.as_ptr() as isize - origin.as_ptr() as isize) / core::mem::size_of::<T>() as isize)]
797808
pub const unsafe fn offset_from(self, origin: NonNull<T>) -> isize
798809
where
799810
T: Sized,
@@ -887,6 +898,13 @@ impl<T: ?Sized> NonNull<T> {
887898
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
888899
#[unstable(feature = "ptr_sub_ptr", issue = "95892")]
889900
#[rustc_const_unstable(feature = "const_ptr_sub_ptr", issue = "95892")]
901+
#[requires(
902+
self.as_ptr().addr().checked_sub(subtracted.as_ptr().addr()).is_some() &&
903+
kani::mem::same_allocation(self.as_ptr(), subtracted.as_ptr()) &&
904+
(self.as_ptr().addr()) >= (subtracted.as_ptr().addr()) &&
905+
(self.as_ptr().addr() - subtracted.as_ptr().addr()) % core::mem::size_of::<T>() == 0
906+
)]
907+
#[ensures(|result: &usize| *result == self.as_ptr().offset_from(subtracted.as_ptr()) as usize)]
890908
pub const unsafe fn sub_ptr(self, subtracted: NonNull<T>) -> usize
891909
where
892910
T: Sized,
@@ -2386,4 +2404,73 @@ mod verify {
23862404
// Perform the alignment check
23872405
let result = non_null_ptr.is_aligned_to(align);
23882406
}
2407+
2408+
#[kani::proof_for_contract(NonNull::sub)]
2409+
pub fn non_null_check_sub() {
2410+
const SIZE: usize = 10000;
2411+
let mut generator = kani::PointerGenerator::<SIZE>::new();
2412+
// Get a raw pointer from the generator within bounds
2413+
let raw_ptr: *mut i32 = generator.any_in_bounds().ptr;
2414+
// Create a non-null pointer from the raw pointer
2415+
let ptr = unsafe { NonNull::new(raw_ptr).unwrap() };
2416+
// Create a non-deterministic count value
2417+
let count: usize = kani::any();
2418+
2419+
unsafe {
2420+
let result = ptr.sub(count);
2421+
}
2422+
}
2423+
2424+
#[kani::proof_for_contract(NonNull::sub_ptr)]
2425+
pub fn non_null_check_sub_ptr() {
2426+
const SIZE: usize = core::mem::size_of::<i32>() * 1000;
2427+
let mut generator1 = kani::PointerGenerator::<SIZE>::new();
2428+
let mut generator2 = kani::PointerGenerator::<SIZE>::new();
2429+
2430+
let ptr: *mut i32 = if kani::any() {
2431+
generator1.any_in_bounds().ptr as *mut i32
2432+
} else {
2433+
generator2.any_in_bounds().ptr as *mut i32
2434+
};
2435+
2436+
let origin: *mut i32 = if kani::any() {
2437+
generator1.any_in_bounds().ptr as *mut i32
2438+
} else {
2439+
generator2.any_in_bounds().ptr as *mut i32
2440+
};
2441+
2442+
let ptr_nonnull = unsafe { NonNull::new(ptr).unwrap() };
2443+
let origin_nonnull = unsafe { NonNull::new(origin).unwrap() };
2444+
2445+
unsafe {
2446+
let distance = ptr_nonnull.sub_ptr(origin_nonnull);
2447+
}
2448+
}
2449+
2450+
#[kani::proof_for_contract(NonNull::offset_from)]
2451+
#[kani::should_panic]
2452+
pub fn non_null_check_offset_from() {
2453+
const SIZE: usize = core::mem::size_of::<i32>() * 1000;
2454+
let mut generator1 = kani::PointerGenerator::<SIZE>::new();
2455+
let mut generator2 = kani::PointerGenerator::<SIZE>::new();
2456+
2457+
let ptr: *mut i32 = if kani::any() {
2458+
generator1.any_in_bounds().ptr as *mut i32
2459+
} else {
2460+
generator2.any_in_bounds().ptr as *mut i32
2461+
};
2462+
2463+
let origin: *mut i32 = if kani::any() {
2464+
generator1.any_in_bounds().ptr as *mut i32
2465+
} else {
2466+
generator2.any_in_bounds().ptr as *mut i32
2467+
};
2468+
2469+
let ptr_nonnull = unsafe { NonNull::new(ptr).unwrap() };
2470+
let origin_nonnull = unsafe { NonNull::new(origin).unwrap() };
2471+
2472+
unsafe {
2473+
let distance = ptr_nonnull.offset_from(origin_nonnull);
2474+
}
2475+
}
23892476
}

0 commit comments

Comments
 (0)