Skip to content

Commit 53739b7

Browse files
committed
new challenges
1 parent dcb4f89 commit 53739b7

File tree

2 files changed

+140
-80
lines changed

2 files changed

+140
-80
lines changed

doc/src/challenges/XXXX-rc.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Challenge XXXX[^challenge_id]: Verify reference-counted Cell implementation
2+
3+
- **Status:** Open
4+
- **Solution:** *Option field to point to the PR that solved this challenge.*
5+
- **Tracking Issue:** *Link to issue*
6+
- **Start date:** 2025/06/01
7+
- **End date:** 2025/12/31
8+
- **Reward:** *TBD*[^reward]
9+
10+
-------------------
11+
12+
13+
## Goal
14+
15+
The goal of this challenge is to verify Rc and its related Weak implementation. Rc is the library-provided building blocks that enable safe multiple ownership of data through reference counting for single-threaded cases, as opposed to the usual ownership types used by Rust. A related challenge verifies the Arc implementation, which is atomic multi-threaded.
16+
17+
## Motivation
18+
19+
The Rc (for single-threaded code) and Arc (atomic multi-threaded) cell types are widely used in Rust programs to enable shared ownership of data through reference counting. Since shared ownership is generally not permitted by Rust's type system, these implementations use unsafe code to bypass Rust's usual compile-time checks. Verifying the Rust standard library thus fundamentally requires verification of these types.
20+
21+
## Description
22+
23+
This challenge verifies a number of Rc methods that encapsulate unsafety, as well as providing contracts for unsafe methods that impose safety conditions on their callers for correct use.
24+
25+
A key part of the Rc implementation is the Weak struct, which allows keeping a temporary reference to an Rc without creating circular references and without protecting the inner value from being dropped.
26+
27+
### Assumptions
28+
29+
Some properties needed for safety are beyond the ability of the Rust type system to express. This is true for all challenges, but we point out some of the properties that are relevant for this challenge.
30+
31+
* It may be possible to use something analogous to the [can_reference API](https://model-checking.github.io/kani/crates/doc/kani/mem/fn.can_dereference.html), which we introduce, to track that a pointer indeed originates from `into_raw`.
32+
33+
* It is unclear how we will show that the reference count is greater than 0 when it is being decremented; the proposed `linked_list` [challenge](0005-linked-list.html) solution does not appear to check list length before performing operations either.
34+
35+
* Showing that something is initialized, as required by `assume_init`, appears to be beyond the current state of the art for type systems, so it may be impossible to express the full safety property required there.
36+
37+
### Success Criteria
38+
39+
All the following pub unsafe functions must be annotated with safety contracts and the contracts have been verified:
40+
41+
| Function | Location |
42+
|---------|---------|
43+
| Rc<mem::MaybeUninit<T>,A>::assume_init | alloc::rc |
44+
| Rc<[mem::MaybeUninit<T>],A>::assume_init | alloc::rc |
45+
| Rc<T:?Sized>::from_raw | alloc::rc |
46+
| Rc<T:?Sized>::increment_strong_count | alloc::rc |
47+
| Rc<T:?Sized>::decrement_strong_count | alloc::rc |
48+
| Rc<T:?Sized,A:Allocator>::from_raw_in | alloc::rc |
49+
| Rc<T:?Sized,A:Allocator>::increment_strong_count_in | alloc::rc |
50+
| Rc<T:?Sized,A:Allocator>::decrement_strong_count_in | alloc::rc |
51+
| Rc<T:?Sized,A:Allocator>::get_mut_unchecked | alloc::rc |
52+
| Rc<dyn Any,A:Allocator>::downcast_unchecked | alloc::rc |
53+
| Weak<T:?Sized>::from_raw | alloc::rc |
54+
| Weak<T:?Sized,A:Allocator>::from_raw_in | alloc::rc |
55+
56+
These (not necessarily public) functions contain unsafe code in their bodies but are not themselves marked unsafe. At least 75% of these should be proven unconditionally safe, or safety contracts should be added.
57+
58+
| Function | Location |
59+
|---------|---------|
60+
| Rc<T:?Sized, A:Allocator>::inner | alloc::rc |
61+
| Rc<T:?Sized, A:Allocator>::into_inner_with_allocator | alloc::rc |
62+
| Rc<T>::new | alloc::rc |
63+
| Rc<T>::new_uninit | alloc::rc |
64+
| Rc<T>::new_zeroed | alloc::rc |
65+
| Rc<T>::try_new | alloc::rc |
66+
| Rc<T>::try_new_uninit | alloc::rc |
67+
| Rc<T>::try_new_zeroed | alloc::rc |
68+
| Rc<T>::pin | alloc::rc |
69+
| Rc<T,A:Allocator>::new_uninit_in | alloc::rc |
70+
| Rc<T,A:Allocator>::new_zeroed_in | alloc::rc |
71+
| Rc<T,A:Allocator>::new_cyclic_in | alloc::rc |
72+
| Rc<T,A:Allocator>::try_new_in | alloc::rc |
73+
| Rc<T,A:Allocator>::try_new_uninit_in | alloc::rc |
74+
| Rc<T,A:Allocator>::try_new_zeroed_in | alloc::rc |
75+
| Rc<T,A:Allocator>::pin_in | alloc::rc |
76+
| Rc<T,A:Allocator>::try_unwrap | alloc::rc |
77+
| Rc<[T]>::new_uninit_slice | alloc::rc |
78+
| Rc<[T]>::new_zeroed_slice | alloc::rc |
79+
| Rc<[T]>::into_array | alloc::rc |
80+
| Rc<[T],A:Allocator>::new_uninit_slice_in | alloc::rc |
81+
| Rc<[T],A:Allocator>::new_zeroed_slice_in | alloc::rc |
82+
| Rc<T:?Sized,A:Allocator>::into_raw_with_allocator | alloc::rc |
83+
| Rc<T:?Sized,A:Allocator>::as_ptr | alloc::rc |
84+
| Rc<T:?Sized,A:Allocator>::get_mut | alloc::rc |
85+
| Rc<T:?Sized+CloneToUninit, A:Allocator+Clone>::make_mut | alloc::rc |
86+
| Rc<dyn Any,A:Allocator>::downcast | alloc::rc |
87+
| Rc<T:?Sized,A:Allocator>::from_box_in | alloc::rc |
88+
| RcFromSlice<T: Clone>::from_slice | alloc::rc |
89+
| RcFromSlice<T: Copy>::from_slice | alloc::rc |
90+
| Drop<T: ?Sized, A:Allocator>::drop for Rc | alloc::rc |
91+
| Clone<T: ?Sized, A:Allocator>::clone for Rc | alloc::rc |
92+
| Default<T:Default>::default | alloc::rc |
93+
| Default<str>::default | alloc::rc |
94+
| From<&Str>::from | alloc::rc |
95+
| From<Vec<T,A:Allocator>>::from | alloc::rc |
96+
| From<Rc<str>>::from | alloc::rc |
97+
| TryFrom<Rc[T],A:Allocator>::try_from | alloc::rc |
98+
| ToRcSlice<T, I>::to_rc_slice | alloc::rc |
99+
| Weak<T:?Sized,A:Allocator>::as_ptr | alloc::rc |
100+
| Weak<T:?Sized,A:Allocator>::into_raw_with_allocator | alloc::rc |
101+
| Weak<T:?Sized,A:Allocator>::upgrade | alloc::rc |
102+
| Weak<T:?Sized,A:Allocator>::inner | alloc::rc |
103+
| Drop<T:?Sized, A:Allocator>::drop for Weak | alloc::rc |
104+
| RcInnerPtr::inc_strong | alloc::rc |
105+
| RcInnerPtr::inc_weak | alloc::rc |
106+
| UniqueRc<T:?Sized,A:Allocator>::into_rc | alloc::rc |
107+
| UniqueRc<T:?Sized,A:Allocator+Clone>::downgrade | alloc::rc |
108+
| Deref<T:?Sized,A:Allocator>::deref | alloc::rc |
109+
| DerefMut<T:?Sized,A:Allocator>::deref_mut | alloc::rc |
110+
| Drop<T:?Sized, A:Allocator>::drop for UniqueRc | alloc::rc |
111+
| UniqueRcUninit<T:?Sized, A:Allocator>::new | alloc::rc |
112+
| UniqueRcUninit<T:?Sized, A:Allocator>::data_ptr | alloc::rc |
113+
| Drop<T:?Sized, A:Allocator>::drop for UniqueRcUninit | alloc::rc |
114+
115+
This list excludes non-public unsafe functions; relevant ones should be called from public unsafe functions.
116+
117+
All proofs must automatically ensure the absence of the following undefined behaviors [ref](https://github.com/rust-lang/reference/blob/142b2ed77d33f37a9973772bd95e6144ed9dce43/src/behavior-considered-undefined.md):
118+
119+
*List of UBs*
120+
121+
Note: All solutions to verification challenges need to satisfy the criteria established in the [challenge book](general-rules.md)
122+
in addition to the ones listed above.
123+
124+
[^challenge_id]: The number of the challenge sorted by publication date.
125+
[^reward]: Leave it as TBD when creating a new challenge. This should only be filled by the reward committee.

doc/src/challenges/new-challenge.md renamed to doc/src/challenges/XXXY-arc.md

Lines changed: 15 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,42 +12,36 @@
1212

1313
## Goal
1414

15-
The goal of this challenge is to verify the Rc, Arc, and their related Weak implementations. Rc and Arc are the library-provided building blocks that enable safe multiple ownership of data through reference counting, as opposed to the usual ownership types used by Rust.
15+
The goal of this challenge is to verify Arc and its related Weak implementation. Arc is the library-provided building blocks that enable safe multiple ownership of data through reference counting for multi-threaded code, as opposed to the usual ownership types used by Rust. The Rc implementation is the subject of a related challenge.
1616

1717
## Motivation
1818

1919
The Rc (for single-threaded code) and Arc (atomic multi-threaded) cell types are widely used in Rust programs to enable shared ownership of data through reference counting. Since shared ownership is generally not permitted by Rust's type system, these implementations use unsafe code to bypass Rust's usual compile-time checks. Verifying the Rust standard library thus fundamentally requires verification of these types.
2020

21-
The verification includes verification of a number of Rc and Arc methods that encapsulate unsafety, as well as providing contracts for unsafe methods that impose safety conditions on their callers for correct use.
22-
23-
A key part of the Rc and Arc implementations is the Weak struct, which allows keeping a temporary reference to an Rc or Arc without creating circular references and without protecting the inner value from being dropped.
24-
2521
## Description
2622

27-
*Describe the challenge in more details.*
23+
This challenge verifies a number of Arc methods that encapsulate unsafety, as well as providing contracts for unsafe methods that impose safety conditions on their callers for correct use.
24+
25+
A key part of the Arc implementation is the Weak struct, which allows keeping a temporary reference to an Arc without creating circular references and without protecting the inner value from being dropped.
2826

2927
### Assumptions
3028

31-
*Mention any assumption that users may make. Example, "assuming the usage of Stacked Borrows".*
29+
Some properties needed for safety are beyond the ability of the Rust type system to express. This is true for all challenges, but we point out some of the properties that are relevant for this challenge.
30+
31+
* It may be possible to use something analogous to the [can_reference API](https://model-checking.github.io/kani/crates/doc/kani/mem/fn.can_dereference.html), which we introduce, to track that a pointer indeed originates from `into_raw`.
32+
33+
* It is unclear how we will show that the reference count is greater than 0 when it is being decremented; the proposed `linked_list` [challenge](0005-linked-list.html) solution does not appear to check list length before performing operations either.
34+
35+
* Showing that something is initialized, as required by `assume_init`, appears to be beyond the current state of the art for type systems, so it may be impossible to express the full safety property required there.
36+
37+
* In general, Kani does not support verifying concurrent code, but it may still be possible to verify the memory-related safety properties here, assuming that the atomicity declarations are sufficient.
3238

3339
### Success Criteria
3440

3541
All the following pub unsafe functions must be annotated with safety contracts and the contracts have been verified:
3642

3743
| Function | Location |
3844
|---------|---------|
39-
| Rc<mem::MaybeUninit<T>,A>::assume_init | alloc::rc |
40-
| Rc<[mem::MaybeUninit<T>],A>::assume_init | alloc::rc |
41-
| Rc<T:?Sized>::from_raw | alloc::rc |
42-
| Rc<T:?Sized>::increment_strong_count | alloc::rc |
43-
| Rc<T:?Sized>::decrement_strong_count | alloc::rc |
44-
| Rc<T:?Sized,A:Allocator>::from_raw_in | alloc::rc |
45-
| Rc<T:?Sized,A:Allocator>::increment_strong_count_in | alloc::rc |
46-
| Rc<T:?Sized,A:Allocator>::decrement_strong_count_in | alloc::rc |
47-
| Rc<T:?Sized,A:Allocator>::get_mut_unchecked | alloc::rc |
48-
| Rc<dyn Any,A:Allocator>::downcast_unchecked | alloc::rc |
49-
| Weak<T:?Sized>::from_raw | alloc::rc |
50-
| Weak<T:?Sized,A:Allocator>::from_raw_in | alloc::rc |
5145
| Arc<mem::MaybeUninit<T>,A>::assume_init | alloc::sync |
5246
| Arc<[mem::MaybeUninit<T>],A>::assume_init | alloc::sync |
5347
| Arc<T:?Sized>::from_raw | alloc::sync |
@@ -61,66 +55,7 @@ All the following pub unsafe functions must be annotated with safety contracts a
6155
| Weak<T:?Sized>::from_raw | alloc::sync |
6256
| Weak<T:?Sized,A:Allocator>::from_raw_in | alloc::sync |
6357

64-
These (not necessarily public) functions contain unsafe code in their bodies but are not themselves marked unsafe. At least 75% of these should be proven unconditionally safe, or safety contracts should be added. From alloc::rc:
65-
66-
| Function | Location |
67-
|---------|---------|
68-
| Rc<T:?Sized, A:Allocator>::inner | alloc::rc |
69-
| Rc<T:?Sized, A:Allocator>::into_inner_with_allocator | alloc::rc |
70-
| Rc<T>::new | alloc::rc |
71-
| Rc<T>::new_uninit | alloc::rc |
72-
| Rc<T>::new_zeroed | alloc::rc |
73-
| Rc<T>::try_new | alloc::rc |
74-
| Rc<T>::try_new_uninit | alloc::rc |
75-
| Rc<T>::try_new_zeroed | alloc::rc |
76-
| Rc<T>::pin | alloc::rc |
77-
| Rc<T,A:Allocator>::new_uninit_in | alloc::rc |
78-
| Rc<T,A:Allocator>::new_zeroed_in | alloc::rc |
79-
| Rc<T,A:Allocator>::new_cyclic_in | alloc::rc |
80-
| Rc<T,A:Allocator>::try_new_in | alloc::rc |
81-
| Rc<T,A:Allocator>::try_new_uninit_in | alloc::rc |
82-
| Rc<T,A:Allocator>::try_new_zeroed_in | alloc::rc |
83-
| Rc<T,A:Allocator>::pin_in | alloc::rc |
84-
| Rc<T,A:Allocator>::try_unwrap | alloc::rc |
85-
| Rc<[T]>::new_uninit_slice | alloc::rc |
86-
| Rc<[T]>::new_zeroed_slice | alloc::rc |
87-
| Rc<[T]>::into_array | alloc::rc |
88-
| Rc<[T],A:Allocator>::new_uninit_slice_in | alloc::rc |
89-
| Rc<[T],A:Allocator>::new_zeroed_slice_in | alloc::rc |
90-
| Rc<T:?Sized,A:Allocator>::into_raw_with_allocator | alloc::rc |
91-
| Rc<T:?Sized,A:Allocator>::as_ptr | alloc::rc |
92-
| Rc<T:?Sized,A:Allocator>::get_mut | alloc::rc |
93-
| Rc<T:?Sized+CloneToUninit, A:Allocator+Clone>::make_mut | alloc::rc |
94-
| Rc<dyn Any,A:Allocator>::downcast | alloc::rc |
95-
| Rc<T:?Sized,A:Allocator>::from_box_in | alloc::rc |
96-
| RcFromSlice<T: Clone>::from_slice | alloc::rc |
97-
| RcFromSlice<T: Copy>::from_slice | alloc::rc |
98-
| Drop<T: ?Sized, A:Allocator>::drop for Rc | alloc::rc |
99-
| Clone<T: ?Sized, A:Allocator>::clone for Rc | alloc::rc |
100-
| Default<T:Default>::default | alloc::rc |
101-
| Default<str>::default | alloc::rc |
102-
| From<&Str>::from | alloc::rc |
103-
| From<Vec<T,A:Allocator>>::from | alloc::rc |
104-
| From<Rc<str>>::from | alloc::rc |
105-
| TryFrom<Rc[T],A:Allocator>::try_from | alloc::rc |
106-
| ToRcSlice<T, I>::to_rc_slice | alloc::rc |
107-
| Weak<T:?Sized,A:Allocator>::as_ptr | alloc::rc |
108-
| Weak<T:?Sized,A:Allocator>::into_raw_with_allocator | alloc::rc |
109-
| Weak<T:?Sized,A:Allocator>::upgrade | alloc::rc |
110-
| Weak<T:?Sized,A:Allocator>::inner | alloc::rc |
111-
| Drop<T:?Sized, A:Allocator>::drop for Weak | alloc::rc |
112-
| RcInnerPtr::inc_strong | alloc::rc |
113-
| RcInnerPtr::inc_weak | alloc::rc |
114-
| UniqueRc<T:?Sized,A:Allocator>::into_rc | alloc::rc |
115-
| UniqueRc<T:?Sized,A:Allocator+Clone>::downgrade | alloc::rc |
116-
| Deref<T:?Sized,A:Allocator>::deref | alloc::rc |
117-
| DerefMut<T:?Sized,A:Allocator>::deref_mut | alloc::rc |
118-
| Drop<T:?Sized, A:Allocator>::drop for UniqueRc | alloc::rc |
119-
| UniqueRcUninit<T:?Sized, A:Allocator>::new | alloc::rc |
120-
| UniqueRcUninit<T:?Sized, A:Allocator>::data_ptr | alloc::rc |
121-
| Drop<T:?Sized, A:Allocator>::drop for UniqueRcUninit | alloc::rc |
122-
123-
And from alloc::sync:
58+
These (not necessarily public) functions contain unsafe code in their bodies but are not themselves marked unsafe. At least 75% of these should be proven unconditionally safe, or safety contracts should be added.
12459

12560
| Function | Location |
12661
|---------|---------|
@@ -184,7 +119,7 @@ And from alloc::sync:
184119
| Drop<T:?Sized, A:Allocator>::drop for UniqueArc | alloc::sync |
185120

186121

187-
(I haven't included non-public unsafe functions here, though I do include non-public functions that contain unsafe. Is that a problem? Also, need to add the Arc variants of all these.)
122+
This list excludes non-public unsafe functions; relevant ones should be called from public unsafe functions.
188123

189124
All proofs must automatically ensure the absence of the following undefined behaviors [ref](https://github.com/rust-lang/reference/blob/142b2ed77d33f37a9973772bd95e6144ed9dce43/src/behavior-considered-undefined.md):
190125

0 commit comments

Comments
 (0)