Skip to content

Commit 05484b8

Browse files
committed
Merge remote-tracking branch 'origin/main' into foundation
2 parents 86fe204 + b0fdecf commit 05484b8

File tree

7 files changed

+222
-3
lines changed

7 files changed

+222
-3
lines changed

doc/src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
- [Challenges](./challenges.md)
1616
- [1: Verify core transmuting methods](./challenges/0001-core-transmutation.md)
17-
- [2: Verify the memory safery of core intrinsics using raw pointers](./challenges/0002-intrinsics-memory.md)
17+
- [2: Verify the memory safety of core intrinsics using raw pointers](./challenges/0002-intrinsics-memory.md)
1818
- [3: Verifying Raw Pointer Arithmetic Operations](./challenges/0003-pointer-arithmentic.md)
1919
- [4: Memory safety of BTreeMap's `btree::node` module](./challenges/0004-btree-node.md)
2020
- [5: Verify functions iterating over inductive data type: `linked_list`](./challenges/0005-linked-list.md)

doc/src/challenges/0003-pointer-arithmentic.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Challenge 3: Verifying Raw Pointer Arithmetic Operations
22

3-
- **Status:** Open
3+
- **Status:** Resolved
44
- **Tracking Issue:** [#76](https://github.com/model-checking/verify-rust-std/issues/76)
55
- **Start date:** *2024/06/24*
6-
- **End date:** *2025/04/10*
6+
- **End date:** *2024/12/11*
77
- **Reward:** *N/A*
8+
- **Contributors:** [Surya Togaru](https://github.com/stogaru), [Yifei Wang](https://github.com/xsxszab), [Szu-Yu Lee](https://github.com/szlee118), [Mayuresh Joshi](https://github.com/MayureshJoshi25)
89

910
-------------------
1011

doc/src/general-rules.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,21 @@ members = [
9090
+ "rahulku"
9191
]
9292
```
93+
94+
Committee members are expected to contribute by reviewing pull requests (all
95+
pull requests review approvals from at least two committee members before they
96+
can be merged).
97+
Reviews of solutions towards challenges should consider at least the following aspects:
98+
99+
1. Does the pull request implement a solution that respects/meets the success
100+
criteria of the challenge?
101+
2. Do the contracts and harnesses incorporate the safety conditions stated in
102+
the documentation (from comments in the code and the
103+
[standard library documentation](https://doc.rust-lang.org/std/index.html))?
104+
Note that we currently focus on safety verification. Pre- and post-conditions
105+
towards functional correctness are acceptable as long as they do not
106+
negatively impact verification of safety, such as over-constraining input
107+
values or causing excessive verification run time.
108+
3. Is the contributed code of adequate quality, idiomatic, and stands a chance
109+
to be accepted into the standard library (to the best of the committee
110+
member's knowledge)?

library/alloc/src/collections/vec_deque/mod.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ mod spec_from_iter;
5858
#[cfg(test)]
5959
mod tests;
6060

61+
#[cfg(kani)]
62+
use core::kani;
63+
6164
/// A double-ended queue implemented with a growable ring buffer.
6265
///
6366
/// The "default" usage of this type as a queue is to use [`push_back`] to add to
@@ -3079,3 +3082,43 @@ impl<T, const N: usize> From<[T; N]> for VecDeque<T> {
30793082
deq
30803083
}
30813084
}
3085+
3086+
#[cfg(kani)]
3087+
#[unstable(feature = "kani", issue = "none")]
3088+
mod verify {
3089+
use core::kani;
3090+
use crate::collections::VecDeque;
3091+
3092+
#[kani::proof]
3093+
fn check_vecdeque_swap() {
3094+
// The array's length is set to an arbitrary value, which defines its size.
3095+
// In this case, implementing a dynamic array is not possible using any_array
3096+
// The more elements in the array the longer the veification time.
3097+
const ARRAY_LEN: usize = 40;
3098+
let mut arr: [u32; ARRAY_LEN] = kani::Arbitrary::any_array();
3099+
let mut deque: VecDeque<u32> = VecDeque::from(arr);
3100+
let len = deque.len();
3101+
3102+
// Generate valid indices within bounds
3103+
let i = kani::any_where(|&x: &usize| x < len);
3104+
let j = kani::any_where(|&x: &usize| x < len);
3105+
3106+
// Capture the elements at i and j before the swap
3107+
let elem_i_before = deque[i];
3108+
let elem_j_before = deque[j];
3109+
3110+
// Perform the swap
3111+
deque.swap(i, j);
3112+
3113+
// Postcondition: Verify elements have swapped places
3114+
assert_eq!(deque[i], elem_j_before);
3115+
assert_eq!(deque[j], elem_i_before);
3116+
3117+
// Ensure other elements remain unchanged
3118+
let k = kani::any_where(|&x: &usize| x < len);
3119+
if k != i && k != j {
3120+
assert!(deque[k] == arr[k]);
3121+
}
3122+
}
3123+
3124+
}

library/alloc/src/vec/mod.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4032,3 +4032,49 @@ impl<T, A: Allocator, const N: usize> TryFrom<Vec<T, A>> for [T; N] {
40324032
Ok(array)
40334033
}
40344034
}
4035+
4036+
#[cfg(kani)]
4037+
#[unstable(feature = "kani", issue = "none")]
4038+
mod verify {
4039+
use core::kani;
4040+
use crate::vec::Vec;
4041+
4042+
// Size chosen for testing the empty vector (0), middle element removal (1)
4043+
// and last element removal (2) cases while keeping verification tractable
4044+
const ARRAY_LEN: usize = 3;
4045+
4046+
#[kani::proof]
4047+
pub fn verify_swap_remove() {
4048+
4049+
// Creating a vector directly from a fixed length arbitrary array
4050+
let mut arr: [i32; ARRAY_LEN] = kani::Arbitrary::any_array();
4051+
let mut vect = Vec::from(&arr);
4052+
4053+
// Recording the original length and a copy of the vector for validation
4054+
let original_len = vect.len();
4055+
let original_vec = vect.clone();
4056+
4057+
// Generating a nondeterministic index which is guaranteed to be within bounds
4058+
let index: usize = kani::any_where(|x| *x < original_len);
4059+
4060+
let removed = vect.swap_remove(index);
4061+
4062+
// Verifying that the length of the vector decreases by one after the operation is performed
4063+
assert!(vect.len() == original_len - 1, "Length should decrease by 1");
4064+
4065+
// Verifying that the removed element matches the original element at the index
4066+
assert!(removed == original_vec[index], "Removed element should match original");
4067+
4068+
// Verifying that the removed index now contains the element originally at the vector's last index if applicable
4069+
if index < original_len - 1 {
4070+
assert!(vect[index] == original_vec[original_len - 1], "Index should contain last element");
4071+
}
4072+
4073+
// Check that all other unaffected elements remain unchanged
4074+
let k = kani::any_where(|&x: &usize| x < original_len - 1);
4075+
if k != index {
4076+
assert!(vect[k] == arr[k]);
4077+
}
4078+
4079+
}
4080+
}

library/core/src/option.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,3 +2572,30 @@ impl<T, const N: usize> [Option<T>; N] {
25722572
self.try_map(core::convert::identity)
25732573
}
25742574
}
2575+
2576+
#[cfg(kani)]
2577+
#[unstable(feature = "kani", issue = "none")]
2578+
mod verify {
2579+
use crate::kani;
2580+
use crate::option::Option;
2581+
2582+
#[kani::proof]
2583+
fn verify_as_slice() {
2584+
if kani::any() {
2585+
// Case 1: Option is Some
2586+
let value: u32 = kani::any();
2587+
let some_option: Option<u32> = Some(value);
2588+
2589+
let slice = some_option.as_slice();
2590+
assert_eq!(slice.len(), 1); // The slice should have exactly one element
2591+
assert_eq!(slice[0], value); // The value in the slice should match
2592+
} else {
2593+
// Case 2: Option is None
2594+
let none_option: Option<u32> = None;
2595+
2596+
let empty_slice = none_option.as_slice();
2597+
assert_eq!(empty_slice.len(), 0); // The slice should be empty
2598+
assert!(empty_slice.is_empty()); // Explicit check for emptiness
2599+
}
2600+
}
2601+
}

library/core/src/ptr/non_null.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,8 +1035,14 @@ impl<T: ?Sized> NonNull<T> {
10351035
/// [`ptr::copy`]: crate::ptr::copy()
10361036
#[inline(always)]
10371037
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1038+
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(dest, count).as_ptr()))]
10381039
#[stable(feature = "non_null_convenience", since = "1.80.0")]
10391040
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
1041+
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
1042+
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(self, count).as_ptr())
1043+
&& ub_checks::can_write(NonNull::slice_from_raw_parts(dest, count).as_ptr()))]
1044+
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
1045+
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
10401046
pub const unsafe fn copy_to(self, dest: NonNull<T>, count: usize)
10411047
where
10421048
T: Sized,
@@ -1055,8 +1061,15 @@ impl<T: ?Sized> NonNull<T> {
10551061
/// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping()
10561062
#[inline(always)]
10571063
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1064+
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(dest, count).as_ptr()))]
10581065
#[stable(feature = "non_null_convenience", since = "1.80.0")]
10591066
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
1067+
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
1068+
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(self, count).as_ptr())
1069+
&& ub_checks::can_write(NonNull::slice_from_raw_parts(dest, count).as_ptr())
1070+
&& ub_checks::maybe_is_nonoverlapping(self.as_ptr() as *const (), dest.as_ptr() as *const (), count, core::mem::size_of::<T>()))]
1071+
#[ensures(|result: &()| ub_checks::can_dereference(self.as_ptr() as *const u8)
1072+
&& ub_checks::can_dereference(dest.as_ptr() as *const u8))]
10601073
pub const unsafe fn copy_to_nonoverlapping(self, dest: NonNull<T>, count: usize)
10611074
where
10621075
T: Sized,
@@ -1075,8 +1088,14 @@ impl<T: ?Sized> NonNull<T> {
10751088
/// [`ptr::copy`]: crate::ptr::copy()
10761089
#[inline(always)]
10771090
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1091+
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(self, count).as_ptr()))]
10781092
#[stable(feature = "non_null_convenience", since = "1.80.0")]
10791093
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
1094+
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
1095+
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(src, count).as_ptr())
1096+
&& ub_checks::can_write(NonNull::slice_from_raw_parts(self, count).as_ptr()))]
1097+
#[ensures(|result: &()| ub_checks::can_dereference(src.as_ptr() as *const u8)
1098+
&& ub_checks::can_dereference(self.as_ptr() as *const u8))]
10801099
pub const unsafe fn copy_from(self, src: NonNull<T>, count: usize)
10811100
where
10821101
T: Sized,
@@ -1095,8 +1114,15 @@ impl<T: ?Sized> NonNull<T> {
10951114
/// [`ptr::copy_nonoverlapping`]: crate::ptr::copy_nonoverlapping()
10961115
#[inline(always)]
10971116
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1117+
#[cfg_attr(kani, kani::modifies(NonNull::slice_from_raw_parts(self, count).as_ptr()))]
10981118
#[stable(feature = "non_null_convenience", since = "1.80.0")]
10991119
#[rustc_const_stable(feature = "const_intrinsic_copy", since = "1.83.0")]
1120+
#[requires(count.checked_mul(core::mem::size_of::<T>()).map_or_else(|| false, |size| size <= isize::MAX as usize)
1121+
&& ub_checks::can_dereference(NonNull::slice_from_raw_parts(src, count).as_ptr())
1122+
&& ub_checks::can_write(NonNull::slice_from_raw_parts(self, count).as_ptr())
1123+
&& ub_checks::maybe_is_nonoverlapping(src.as_ptr() as *const (), self.as_ptr() as *const (), count, core::mem::size_of::<T>()))]
1124+
#[ensures(|result: &()| ub_checks::can_dereference(src.as_ptr() as *const u8)
1125+
&& ub_checks::can_dereference(self.as_ptr() as *const u8))]
11001126
pub const unsafe fn copy_from_nonoverlapping(self, src: NonNull<T>, count: usize)
11011127
where
11021128
T: Sized,
@@ -2538,4 +2564,62 @@ mod verify {
25382564
let _ = ptr.byte_offset_from(origin);
25392565
}
25402566
}
2567+
2568+
#[kani::proof_for_contract(NonNull::<T>::copy_to)]
2569+
pub fn non_null_check_copy_to() {
2570+
// PointerGenerator instance
2571+
let mut generator = PointerGenerator::<16>::new();
2572+
// Generate arbitrary pointers for src and dest
2573+
let src_ptr = generator.any_in_bounds::<i32>().ptr;
2574+
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
2575+
// Wrap the raw pointers in NonNull
2576+
let src = NonNull::new(src_ptr).unwrap();
2577+
let dest = NonNull::new(dest_ptr).unwrap();
2578+
// Generate an arbitrary count using kani::any
2579+
let count: usize = kani::any();
2580+
unsafe { src.copy_to(dest, count);}
2581+
}
2582+
2583+
#[kani::proof_for_contract(NonNull::<T>::copy_from)]
2584+
pub fn non_null_check_copy_from() {
2585+
// PointerGenerator instance
2586+
let mut generator = PointerGenerator::<16>::new();
2587+
// Generate arbitrary pointers for src and dest
2588+
let src_ptr = generator.any_in_bounds::<i32>().ptr;
2589+
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
2590+
// Wrap the raw pointers in NonNull
2591+
let src = NonNull::new(src_ptr).unwrap();
2592+
let dest = NonNull::new(dest_ptr).unwrap();
2593+
// Generate an arbitrary count using kani::any
2594+
let count: usize = kani::any();
2595+
unsafe { src.copy_from(dest, count);}
2596+
}
2597+
#[kani::proof_for_contract(NonNull::<T>::copy_to_nonoverlapping)]
2598+
pub fn non_null_check_copy_to_nonoverlapping() {
2599+
// PointerGenerator instance
2600+
let mut generator = PointerGenerator::<16>::new();
2601+
// Generate arbitrary pointers for src and dest
2602+
let src_ptr = generator.any_in_bounds::<i32>().ptr;
2603+
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
2604+
// Wrap the raw pointers in NonNull
2605+
let src = NonNull::new(src_ptr).unwrap();
2606+
let dest = NonNull::new(dest_ptr).unwrap();
2607+
// Generate an arbitrary count using kani::any
2608+
let count: usize = kani::any();
2609+
unsafe { src.copy_to_nonoverlapping(dest, count);}
2610+
}
2611+
#[kani::proof_for_contract(NonNull::<T>::copy_from_nonoverlapping)]
2612+
pub fn non_null_check_copy_from_nonoverlapping() {
2613+
// PointerGenerator instance
2614+
let mut generator = PointerGenerator::<16>::new();
2615+
// Generate arbitrary pointers for src and dest
2616+
let src_ptr = generator.any_in_bounds::<i32>().ptr;
2617+
let dest_ptr = generator.any_in_bounds::<i32>().ptr;
2618+
// Wrap the raw pointers in NonNull
2619+
let src = NonNull::new(src_ptr).unwrap();
2620+
let dest = NonNull::new(dest_ptr).unwrap();
2621+
// Generate an arbitrary count using kani::any
2622+
let count: usize = kani::any();
2623+
unsafe { src.copy_from_nonoverlapping(dest, count);}
2624+
}
25412625
}

0 commit comments

Comments
 (0)