Skip to content

Commit 955577c

Browse files
stogaruxsxszabMayureshJoshi25tautschnigszlee118
authored
Proofs for Vec::swap_remove, Option::as_slice, and VecDeque::swap (#212)
Resolves: #76 ### Changes * Adds proofs for the following functions using raw pointer operations: * `Vec::swap_remove` * `Option::as_slice` * `VecDeque::swap` * ideally the usages should have been verified by stubbing the contracts for reaw pointer operations like `byte_add`, `add` and `offset`, but stubbing cannot be done for these functions at this time due to model-checking/kani#3732 * Marks Challenge 3 as Resolved and changes its end date. * Adds contributors. #### PoCs: * `Vec::swap_remove`: @MayureshJoshi25 * `Option::as_slice`, `VecDeque::swap`: @stogaru 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]> Co-authored-by: Michael Tautschnig <[email protected]> Co-authored-by: szlee118 <[email protected]> Co-authored-by: szlee118 <[email protected]> Co-authored-by: Felipe R. Monteiro <[email protected]>
1 parent ea7a95f commit 955577c

File tree

4 files changed

+119
-2
lines changed

4 files changed

+119
-2
lines changed

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

+3-2
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

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

+43
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

+46
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

+27
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+
}

0 commit comments

Comments
 (0)