Skip to content

Commit dd75225

Browse files
stogaruxsxszab
andauthored
Contract and Harnesses for byte_add, byte_sub and byte_offset (#169)
Towards #76 ### Changes * Adds contracts for `<*mut T>::byte_add`, `<*mut T>::byte_sub` and `<*mut T>::byte_offset`. * Adds harnesses for the function verifying the following pointee types: * All integer types * Tuples (composite types) * Unit Type * Slices * Accomplishes this using a few macros. 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: Yifei Wang <[email protected]>
1 parent 30c756d commit dd75225

File tree

2 files changed

+597
-1
lines changed

2 files changed

+597
-1
lines changed

library/core/src/ptr/const_ptr.rs

+303-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use safety::{ensures, requires};
77

88
#[cfg(kani)]
99
use crate::kani;
10+
use core::mem;
1011

1112
impl<T: ?Sized> *const T {
1213
/// Returns `true` if the pointer is null.
@@ -474,6 +475,20 @@ impl<T: ?Sized> *const T {
474475
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
475476
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
476477
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
478+
#[requires(
479+
// If count is zero, any pointer is valid including null pointer.
480+
(count == 0) ||
481+
// Else if count is not zero, then ensure that adding `count` doesn't cause
482+
// overflow and that both pointers `self` and the result are in the same
483+
// allocation
484+
((self.addr() as isize).checked_add(count).is_some() &&
485+
core::ub_checks::same_allocation(self, self.wrapping_byte_offset(count)))
486+
)]
487+
#[ensures(|&result|
488+
// The resulting pointer should either be unchanged or still point to the same allocation
489+
(self.addr() == result.addr()) ||
490+
(core::ub_checks::same_allocation(self, result))
491+
)]
477492
pub const unsafe fn byte_offset(self, count: isize) -> Self {
478493
// SAFETY: the caller must uphold the safety contract for `offset`.
479494
unsafe { self.cast::<u8>().offset(count).with_metadata_of(self) }
@@ -1012,6 +1027,20 @@ impl<T: ?Sized> *const T {
10121027
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
10131028
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
10141029
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1030+
#[requires(
1031+
// If count is zero, any pointer is valid including null pointer.
1032+
(count == 0) ||
1033+
// Else if count is not zero, then ensure that adding `count` doesn't cause
1034+
// overflow and that both pointers `self` and the result are in the same
1035+
// allocation
1036+
((self.addr() as isize).checked_add(count as isize).is_some() &&
1037+
core::ub_checks::same_allocation(self, self.wrapping_byte_add(count)))
1038+
)]
1039+
#[ensures(|&result|
1040+
// The resulting pointer should either be unchanged or still point to the same allocation
1041+
(self.addr() == result.addr()) ||
1042+
(core::ub_checks::same_allocation(self, result))
1043+
)]
10151044
pub const unsafe fn byte_add(self, count: usize) -> Self {
10161045
// SAFETY: the caller must uphold the safety contract for `add`.
10171046
unsafe { self.cast::<u8>().add(count).with_metadata_of(self) }
@@ -1142,6 +1171,20 @@ impl<T: ?Sized> *const T {
11421171
#[stable(feature = "pointer_byte_offsets", since = "1.75.0")]
11431172
#[rustc_const_stable(feature = "const_pointer_byte_offsets", since = "1.75.0")]
11441173
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1174+
#[requires(
1175+
// If count is zero, any pointer is valid including null pointer.
1176+
(count == 0) ||
1177+
// Else if count is not zero, then ensure that subtracting `count` doesn't
1178+
// cause overflow and that both pointers `self` and the result are in the
1179+
// same allocation
1180+
((self.addr() as isize).checked_sub(count as isize).is_some() &&
1181+
core::ub_checks::same_allocation(self, self.wrapping_byte_sub(count)))
1182+
)]
1183+
#[ensures(|&result|
1184+
// The resulting pointer should either be unchanged or still point to the same allocation
1185+
(self.addr() == result.addr()) ||
1186+
(core::ub_checks::same_allocation(self, result))
1187+
)]
11451188
pub const unsafe fn byte_sub(self, count: usize) -> Self {
11461189
// SAFETY: the caller must uphold the safety contract for `sub`.
11471190
unsafe { self.cast::<u8>().sub(count).with_metadata_of(self) }
@@ -2068,7 +2111,9 @@ mod verify {
20682111
check_const_offset_slice_tuple_4
20692112
);
20702113

2071-
// Array size bound for kani::any_array for `offset_from` verification
2114+
// The array's length is set to an arbitrary value, which defines its size.
2115+
// In this case, implementing a dynamic array is not possible, because
2116+
// PointerGenerator does not support dynamic sized arrays.
20722117
const ARRAY_LEN: usize = 40;
20732118

20742119
macro_rules! generate_offset_from_harness {
@@ -2206,4 +2251,261 @@ mod verify {
22062251
check_const_offset_from_tuple_4,
22072252
check_const_offset_from_tuple_4_arr
22082253
);
2254+
2255+
#[kani::proof_for_contract(<*const ()>::byte_offset)]
2256+
#[kani::should_panic]
2257+
pub fn check_const_byte_offset_unit_invalid_count() {
2258+
let val = ();
2259+
let ptr: *const () = &val;
2260+
let count: isize = kani::any_where(|&x| x != (mem::size_of::<()>() as isize));
2261+
unsafe {
2262+
ptr.byte_offset(count);
2263+
}
2264+
}
2265+
2266+
#[kani::proof_for_contract(<*const ()>::byte_offset)]
2267+
pub fn check_const_byte_offset_cast_unit() {
2268+
let mut generator = PointerGenerator::<ARRAY_LEN>::new();
2269+
let ptr: *const u8 = generator.any_in_bounds().ptr;
2270+
let ptr1: *const () = ptr as *const ();
2271+
let count: isize = kani::any();
2272+
unsafe {
2273+
ptr1.byte_offset(count);
2274+
}
2275+
}
2276+
2277+
// generate proof for contracts of byte_add, byte_sub and byte_offset to verify
2278+
// unit pointee type
2279+
// - `$fn_name`: function for which the contract must be verified
2280+
// - `$proof_name`: name of the harness generated
2281+
macro_rules! gen_const_byte_arith_harness_for_unit {
2282+
(byte_offset, $proof_name:ident) => {
2283+
#[kani::proof_for_contract(<*const ()>::byte_offset)]
2284+
pub fn $proof_name() {
2285+
let val = ();
2286+
let ptr: *const () = &val;
2287+
let count: isize = mem::size_of::<()>() as isize;
2288+
unsafe {
2289+
ptr.byte_offset(count);
2290+
}
2291+
}
2292+
};
2293+
2294+
($fn_name:ident, $proof_name:ident) => {
2295+
#[kani::proof_for_contract(<*const ()>::$fn_name)]
2296+
pub fn $proof_name() {
2297+
let val = ();
2298+
let ptr: *const () = &val;
2299+
//byte_add and byte_sub need count to be usize unlike byte_offset
2300+
let count: usize = mem::size_of::<()>();
2301+
unsafe {
2302+
ptr.$fn_name(count);
2303+
}
2304+
}
2305+
};
2306+
}
2307+
2308+
gen_const_byte_arith_harness_for_unit!(byte_add, check_const_byte_add_unit);
2309+
gen_const_byte_arith_harness_for_unit!(byte_sub, check_const_byte_sub_unit);
2310+
gen_const_byte_arith_harness_for_unit!(byte_offset, check_const_byte_offset_unit);
2311+
2312+
// generate proof for contracts for byte_add, byte_sub and byte_offset
2313+
// - `$type`: pointee type
2314+
// - `$fn_name`: function for which the contract must be verified
2315+
// - `$proof_name`: name of the harness generated
2316+
macro_rules! gen_const_byte_arith_harness {
2317+
($type:ty, byte_offset, $proof_name:ident) => {
2318+
#[kani::proof_for_contract(<*const $type>::byte_offset)]
2319+
pub fn $proof_name() {
2320+
// generator with space for single element
2321+
let mut generator1 = PointerGenerator::<{ mem::size_of::<$type>() }>::new();
2322+
// generator with space for multiple elements
2323+
let mut generator2 =
2324+
PointerGenerator::<{ mem::size_of::<$type>() * ARRAY_LEN }>::new();
2325+
2326+
let ptr: *const $type = if kani::any() {
2327+
generator1.any_in_bounds().ptr
2328+
} else {
2329+
generator2.any_in_bounds().ptr
2330+
};
2331+
2332+
let count: isize = kani::any();
2333+
2334+
unsafe {
2335+
ptr.byte_offset(count);
2336+
}
2337+
}
2338+
};
2339+
2340+
($type:ty, $fn_name:ident, $proof_name:ident) => {
2341+
#[kani::proof_for_contract(<*const $type>::$fn_name)]
2342+
pub fn $proof_name() {
2343+
// generator with space for single element
2344+
let mut generator1 = PointerGenerator::<{ mem::size_of::<$type>() }>::new();
2345+
// generator with space for multiple elements
2346+
let mut generator2 =
2347+
PointerGenerator::<{ mem::size_of::<$type>() * ARRAY_LEN }>::new();
2348+
2349+
let ptr: *const $type = if kani::any() {
2350+
generator1.any_in_bounds().ptr
2351+
} else {
2352+
generator2.any_in_bounds().ptr
2353+
};
2354+
2355+
//byte_add and byte_sub need count to be usize unlike byte_offset
2356+
let count: usize = kani::any();
2357+
2358+
unsafe {
2359+
ptr.$fn_name(count);
2360+
}
2361+
}
2362+
};
2363+
}
2364+
2365+
gen_const_byte_arith_harness!(i8, byte_add, check_const_byte_add_i8);
2366+
gen_const_byte_arith_harness!(i16, byte_add, check_const_byte_add_i16);
2367+
gen_const_byte_arith_harness!(i32, byte_add, check_const_byte_add_i32);
2368+
gen_const_byte_arith_harness!(i64, byte_add, check_const_byte_add_i64);
2369+
gen_const_byte_arith_harness!(i128, byte_add, check_const_byte_add_i128);
2370+
gen_const_byte_arith_harness!(isize, byte_add, check_const_byte_add_isize);
2371+
gen_const_byte_arith_harness!(u8, byte_add, check_const_byte_add_u8);
2372+
gen_const_byte_arith_harness!(u16, byte_add, check_const_byte_add_u16);
2373+
gen_const_byte_arith_harness!(u32, byte_add, check_const_byte_add_u32);
2374+
gen_const_byte_arith_harness!(u64, byte_add, check_const_byte_add_u64);
2375+
gen_const_byte_arith_harness!(u128, byte_add, check_const_byte_add_u128);
2376+
gen_const_byte_arith_harness!(usize, byte_add, check_const_byte_add_usize);
2377+
gen_const_byte_arith_harness!((i8, i8), byte_add, check_const_byte_add_tuple_1);
2378+
gen_const_byte_arith_harness!((f64, bool), byte_add, check_const_byte_add_tuple_2);
2379+
gen_const_byte_arith_harness!((i32, f64, bool), byte_add, check_const_byte_add_tuple_3);
2380+
gen_const_byte_arith_harness!(
2381+
(i8, u16, i32, u64, isize),
2382+
byte_add,
2383+
check_const_byte_add_tuple_4
2384+
);
2385+
2386+
gen_const_byte_arith_harness!(i8, byte_sub, check_const_byte_sub_i8);
2387+
gen_const_byte_arith_harness!(i16, byte_sub, check_const_byte_sub_i16);
2388+
gen_const_byte_arith_harness!(i32, byte_sub, check_const_byte_sub_i32);
2389+
gen_const_byte_arith_harness!(i64, byte_sub, check_const_byte_sub_i64);
2390+
gen_const_byte_arith_harness!(i128, byte_sub, check_const_byte_sub_i128);
2391+
gen_const_byte_arith_harness!(isize, byte_sub, check_const_byte_sub_isize);
2392+
gen_const_byte_arith_harness!(u8, byte_sub, check_const_byte_sub_u8);
2393+
gen_const_byte_arith_harness!(u16, byte_sub, check_const_byte_sub_u16);
2394+
gen_const_byte_arith_harness!(u32, byte_sub, check_const_byte_sub_u32);
2395+
gen_const_byte_arith_harness!(u64, byte_sub, check_const_byte_sub_u64);
2396+
gen_const_byte_arith_harness!(u128, byte_sub, check_const_byte_sub_u128);
2397+
gen_const_byte_arith_harness!(usize, byte_sub, check_const_byte_sub_usize);
2398+
gen_const_byte_arith_harness!((i8, i8), byte_sub, check_const_byte_sub_tuple_1);
2399+
gen_const_byte_arith_harness!((f64, bool), byte_sub, check_const_byte_sub_tuple_2);
2400+
gen_const_byte_arith_harness!((i32, f64, bool), byte_sub, check_const_byte_sub_tuple_3);
2401+
gen_const_byte_arith_harness!(
2402+
(i8, u16, i32, u64, isize),
2403+
byte_sub,
2404+
check_const_byte_sub_tuple_4
2405+
);
2406+
2407+
gen_const_byte_arith_harness!(i8, byte_offset, check_const_byte_offset_i8);
2408+
gen_const_byte_arith_harness!(i16, byte_offset, check_const_byte_offset_i16);
2409+
gen_const_byte_arith_harness!(i32, byte_offset, check_const_byte_offset_i32);
2410+
gen_const_byte_arith_harness!(i64, byte_offset, check_const_byte_offset_i64);
2411+
gen_const_byte_arith_harness!(i128, byte_offset, check_const_byte_offset_i128);
2412+
gen_const_byte_arith_harness!(isize, byte_offset, check_const_byte_offset_isize);
2413+
gen_const_byte_arith_harness!(u8, byte_offset, check_const_byte_offset_u8);
2414+
gen_const_byte_arith_harness!(u16, byte_offset, check_const_byte_offset_u16);
2415+
gen_const_byte_arith_harness!(u32, byte_offset, check_const_byte_offset_u32);
2416+
gen_const_byte_arith_harness!(u64, byte_offset, check_const_byte_offset_u64);
2417+
gen_const_byte_arith_harness!(u128, byte_offset, check_const_byte_offset_u128);
2418+
gen_const_byte_arith_harness!(usize, byte_offset, check_const_byte_offset_usize);
2419+
gen_const_byte_arith_harness!((i8, i8), byte_offset, check_const_byte_offset_tuple_1);
2420+
gen_const_byte_arith_harness!((f64, bool), byte_offset, check_const_byte_offset_tuple_2);
2421+
gen_const_byte_arith_harness!(
2422+
(i32, f64, bool),
2423+
byte_offset,
2424+
check_const_byte_offset_tuple_3
2425+
);
2426+
gen_const_byte_arith_harness!(
2427+
(i8, u16, i32, u64, isize),
2428+
byte_offset,
2429+
check_const_byte_offset_tuple_4
2430+
);
2431+
2432+
macro_rules! gen_const_byte_arith_harness_for_slice {
2433+
($type:ty, byte_offset, $proof_name:ident) => {
2434+
#[kani::proof_for_contract(<*const [$type]>::byte_offset)]
2435+
pub fn $proof_name() {
2436+
let arr: [$type; ARRAY_LEN] = kani::Arbitrary::any_array();
2437+
let slice: &[$type] = kani::slice::any_slice_of_array(&arr);
2438+
let ptr: *const [$type] = slice;
2439+
2440+
let count: isize = kani::any();
2441+
2442+
unsafe {
2443+
ptr.byte_offset(count);
2444+
}
2445+
}
2446+
};
2447+
2448+
($type:ty, $fn_name: ident, $proof_name:ident) => {
2449+
#[kani::proof_for_contract(<*const [$type]>::$fn_name)]
2450+
pub fn $proof_name() {
2451+
let arr: [$type; ARRAY_LEN] = kani::Arbitrary::any_array();
2452+
let slice: &[$type] = kani::slice::any_slice_of_array(&arr);
2453+
let ptr: *const [$type] = slice;
2454+
2455+
//byte_add and byte_sub need count to be usize unlike byte_offset
2456+
let count: usize = kani::any();
2457+
2458+
unsafe {
2459+
ptr.$fn_name(count);
2460+
}
2461+
}
2462+
};
2463+
}
2464+
2465+
gen_const_byte_arith_harness_for_slice!(i8, byte_add, check_const_byte_add_i8_slice);
2466+
gen_const_byte_arith_harness_for_slice!(i16, byte_add, check_const_byte_add_i16_slice);
2467+
gen_const_byte_arith_harness_for_slice!(i32, byte_add, check_const_byte_add_i32_slice);
2468+
gen_const_byte_arith_harness_for_slice!(i64, byte_add, check_const_byte_add_i64_slice);
2469+
gen_const_byte_arith_harness_for_slice!(i128, byte_add, check_const_byte_add_i128_slice);
2470+
gen_const_byte_arith_harness_for_slice!(isize, byte_add, check_const_byte_add_isize_slice);
2471+
gen_const_byte_arith_harness_for_slice!(u8, byte_add, check_const_byte_add_u8_slice);
2472+
gen_const_byte_arith_harness_for_slice!(u16, byte_add, check_const_byte_add_u16_slice);
2473+
gen_const_byte_arith_harness_for_slice!(u32, byte_add, check_const_byte_add_u32_slice);
2474+
gen_const_byte_arith_harness_for_slice!(u64, byte_add, check_const_byte_add_u64_slice);
2475+
gen_const_byte_arith_harness_for_slice!(u128, byte_add, check_const_byte_add_u128_slice);
2476+
gen_const_byte_arith_harness_for_slice!(usize, byte_add, check_const_byte_add_usize_slice);
2477+
2478+
gen_const_byte_arith_harness_for_slice!(i8, byte_sub, check_const_byte_sub_i8_slice);
2479+
gen_const_byte_arith_harness_for_slice!(i16, byte_sub, check_const_byte_sub_i16_slice);
2480+
gen_const_byte_arith_harness_for_slice!(i32, byte_sub, check_const_byte_sub_i32_slice);
2481+
gen_const_byte_arith_harness_for_slice!(i64, byte_sub, check_const_byte_sub_i64_slice);
2482+
gen_const_byte_arith_harness_for_slice!(i128, byte_sub, check_const_byte_sub_i128_slice);
2483+
gen_const_byte_arith_harness_for_slice!(isize, byte_sub, check_const_byte_sub_isize_slice);
2484+
gen_const_byte_arith_harness_for_slice!(u8, byte_sub, check_const_byte_sub_u8_slice);
2485+
gen_const_byte_arith_harness_for_slice!(u16, byte_sub, check_const_byte_sub_u16_slice);
2486+
gen_const_byte_arith_harness_for_slice!(u32, byte_sub, check_const_byte_sub_u32_slice);
2487+
gen_const_byte_arith_harness_for_slice!(u64, byte_sub, check_const_byte_sub_u64_slice);
2488+
gen_const_byte_arith_harness_for_slice!(u128, byte_sub, check_const_byte_sub_u128_slice);
2489+
gen_const_byte_arith_harness_for_slice!(usize, byte_sub, check_const_byte_sub_usize_slice);
2490+
2491+
gen_const_byte_arith_harness_for_slice!(i8, byte_offset, check_const_byte_offset_i8_slice);
2492+
gen_const_byte_arith_harness_for_slice!(i16, byte_offset, check_const_byte_offset_i16_slice);
2493+
gen_const_byte_arith_harness_for_slice!(i32, byte_offset, check_const_byte_offset_i32_slice);
2494+
gen_const_byte_arith_harness_for_slice!(i64, byte_offset, check_const_byte_offset_i64_slice);
2495+
gen_const_byte_arith_harness_for_slice!(i128, byte_offset, check_const_byte_offset_i128_slice);
2496+
gen_const_byte_arith_harness_for_slice!(
2497+
isize,
2498+
byte_offset,
2499+
check_const_byte_offset_isize_slice
2500+
);
2501+
gen_const_byte_arith_harness_for_slice!(u8, byte_offset, check_const_byte_offset_u8_slice);
2502+
gen_const_byte_arith_harness_for_slice!(u16, byte_offset, check_const_byte_offset_u16_slice);
2503+
gen_const_byte_arith_harness_for_slice!(u32, byte_offset, check_const_byte_offset_u32_slice);
2504+
gen_const_byte_arith_harness_for_slice!(u64, byte_offset, check_const_byte_offset_u64_slice);
2505+
gen_const_byte_arith_harness_for_slice!(u128, byte_offset, check_const_byte_offset_u128_slice);
2506+
gen_const_byte_arith_harness_for_slice!(
2507+
usize,
2508+
byte_offset,
2509+
check_const_byte_offset_usize_slice
2510+
);
22092511
}

0 commit comments

Comments
 (0)