Skip to content

Commit 815db0a

Browse files
authored
Merge branch 'model-checking:main' into unsafe-finder-tool
2 parents 7abf347 + afb5c2c commit 815db0a

File tree

320 files changed

+12174
-7676
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

320 files changed

+12174
-7676
lines changed

.github/workflows/kani-metrics.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ jobs:
2626
python-version: '3.x'
2727

2828
- name: Compute Kani Metrics
29-
run: ./scripts/run-kani.sh --run metrics --with-autoharness --path ${{github.workspace}}
29+
run: |
30+
./scripts/run-kani.sh --run metrics --with-autoharness --path ${{github.workspace}}
31+
rm kani-list.json
3032
3133
- name: Create Pull Request
3234
uses: peter-evans/create-pull-request@v7

.github/workflows/kani.yml

Lines changed: 130 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
- name: Run Kani Verification
5454
run: head/scripts/run-kani.sh --path ${{github.workspace}}/head
5555

56-
kani-autoharness:
56+
kani_autoharness:
5757
name: Verify std library using autoharness
5858
runs-on: ${{ matrix.os }}
5959
strategy:
@@ -78,9 +78,16 @@ jobs:
7878
# possible functions as that may take a lot longer than expected. Instead,
7979
# explicitly list all functions (or prefixes thereof) the proofs of which
8080
# are known to pass.
81+
# Notes:
82+
# - We use >::disjoint_bitor (and >::unchecked_disjoint_bitor) as pattern
83+
# as whitespace is not supported, cf.
84+
# https://github.com/model-checking/kani/issues/4046
8185
- name: Run Kani Verification
8286
run: |
8387
scripts/run-kani.sh --run autoharness --kani-args \
88+
--include-pattern ">::disjoint_bitor" \
89+
--include-pattern ">::unchecked_disjoint_bitor" \
90+
--include-pattern alloc::__default_lib_allocator:: \
8491
--include-pattern alloc::layout::Layout::from_size_align \
8592
--include-pattern ascii::ascii_char::AsciiChar::from_u8 \
8693
--include-pattern char::convert::from_u32_unchecked \
@@ -122,9 +129,123 @@ jobs:
122129
--exclude-pattern time::Duration::from_secs_f \
123130
--include-pattern unicode::unicode_data::conversions::to_ \
124131
--exclude-pattern ::precondition_check \
125-
--harness-timeout 5m \
132+
--harness-timeout 10m \
126133
--default-unwind 1000 \
127-
--jobs=3 --output-format=terse
134+
--jobs=3 --output-format=terse | tee autoharness-verification.log
135+
gzip autoharness-verification.log
136+
137+
- name: Upload Autoharness Verification Log
138+
uses: actions/upload-artifact@v4
139+
with:
140+
name: ${{ matrix.os }}-autoharness-verification.log.gz
141+
path: autoharness-verification.log.gz
142+
if-no-files-found: error
143+
# Aggressively short retention: we don't really need these
144+
retention-days: 3
145+
146+
run_kani_metrics:
147+
name: Kani Metrics
148+
runs-on: ${{ matrix.os }}
149+
strategy:
150+
matrix:
151+
os: [ubuntu-latest, macos-latest]
152+
include:
153+
- os: ubuntu-latest
154+
base: ubuntu
155+
- os: macos-latest
156+
base: macos
157+
fail-fast: true
158+
159+
steps:
160+
# Step 1: Check out the repository
161+
- name: Checkout Repository
162+
uses: actions/checkout@v4
163+
with:
164+
submodules: true
165+
166+
# The Kani metrics collection uses a Python script (kani_std_analysis.py), so make sure Python is installed
167+
- name: Set up Python
168+
uses: actions/setup-python@v4
169+
with:
170+
python-version: '3.x'
171+
172+
# Step 2: Run list on the std library
173+
- name: Run Kani Metrics
174+
run: |
175+
scripts/run-kani.sh --run metrics --with-autoharness
176+
pushd /tmp/std_lib_analysis
177+
tar czf results.tar.gz results
178+
popd
179+
180+
- name: Upload kani-list.json
181+
uses: actions/upload-artifact@v4
182+
with:
183+
name: ${{ matrix.os }}-kani-list.json
184+
path: kani-list.json
185+
if-no-files-found: error
186+
# Aggressively short retention: we don't really need these
187+
retention-days: 3
188+
189+
- name: Upload scanner results
190+
uses: actions/upload-artifact@v4
191+
with:
192+
name: ${{ matrix.os }}-results.tar.gz
193+
path: /tmp/std_lib_analysis/results.tar.gz
194+
if-no-files-found: error
195+
# Aggressively short retention: we don't really need these
196+
retention-days: 3
197+
198+
run-log-analysis:
199+
name: Build JSON from logs
200+
needs: [run_kani_metrics, kani_autoharness]
201+
runs-on: ${{ matrix.os }}
202+
strategy:
203+
matrix:
204+
os: [ubuntu-latest, macos-latest]
205+
include:
206+
- os: ubuntu-latest
207+
base: ubuntu
208+
- os: macos-latest
209+
base: macos
210+
fail-fast: false
211+
212+
steps:
213+
- name: Checkout Repository
214+
uses: actions/checkout@v4
215+
with:
216+
submodules: false
217+
218+
- name: Download log
219+
uses: actions/download-artifact@v4
220+
with:
221+
name: ${{ matrix.os }}-autoharness-verification.log.gz
222+
223+
- name: Download kani-list.json
224+
uses: actions/download-artifact@v4
225+
with:
226+
name: ${{ matrix.os }}-kani-list.json
227+
228+
- name: Download scanner results
229+
uses: actions/download-artifact@v4
230+
with:
231+
name: ${{ matrix.os }}-results.tar.gz
232+
233+
- name: Run log parser
234+
run: |
235+
gunzip autoharness-verification.log.gz
236+
tar xzf results.tar.gz
237+
python3 scripts/kani-std-analysis/log_parser.py \
238+
--kani-list-file kani-list.json \
239+
--analysis-results-dir results/ \
240+
autoharness-verification.log \
241+
-o results.json
242+
243+
- name: Upload JSON
244+
uses: actions/upload-artifact@v4
245+
with:
246+
name: ${{ matrix.os }}-results.json
247+
path: results.json
248+
if-no-files-found: error
128249

129250
run-kani-list:
130251
name: Kani List
@@ -176,12 +297,14 @@ jobs:
176297
# Step 3: Add output to job summary
177298
- name: Add Autoharness Analyzer output to job summary
178299
run: |
300+
pushd scripts/autoharness_analyzer
179301
echo "# Autoharness Failure Summary" >> "$GITHUB_STEP_SUMMARY"
180302
echo "## Crate core, all functions" >> "$GITHUB_STEP_SUMMARY"
181-
cat autoharness_analyzer/core_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
303+
cat core_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
182304
echo "## Crate core, unsafe functions" >> "$GITHUB_STEP_SUMMARY"
183-
cat autoharness_analyzer/core_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
305+
cat core_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
184306
echo "## Crate std, all functions" >> "$GITHUB_STEP_SUMMARY"
185-
cat autoharness_analyzer/std_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
307+
cat std_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
186308
echo "## Crate std, unsafe functions" >> "$GITHUB_STEP_SUMMARY"
187-
cat autoharness_analyzer/std_unsafe_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
309+
cat std_unsafe_autoharness_data.md >> "$GITHUB_STEP_SUMMARY"
310+
popd

.github/workflows/pr_approval.yml

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,21 @@ jobs:
7373
return;
7474
}
7575
76+
// Get the list of changed files
77+
const { data: changedFiles } = await github.rest.pulls.listFiles({
78+
owner,
79+
repo,
80+
pull_number,
81+
});
82+
83+
// Check if any files in doc/, library/ or verifast-proofs/ are modified
84+
const affectsDocs = changedFiles.some(file => file.filename.startsWith('doc/'));
85+
const affectsLibrary = changedFiles.some(file => file.filename.startsWith('library/'));
86+
const affectsVerifast = changedFiles.some(file => file.filename.startsWith('verifast-proofs/'));
87+
// Require two approvals iff one of the above folders are modified; otherwise, one is sufficient.
88+
const requiresTwoApprovals = affectsDocs || affectsLibrary || affectsVerifast;
89+
const requiredApprovals = requiresTwoApprovals ? 2 : 1;
90+
7691
// Get all reviews with pagination
7792
async function getAllReviews() {
7893
let allReviews = [];
@@ -113,23 +128,27 @@ jobs:
113128
.map(review => review.user.login)
114129
);
115130
116-
const requiredApprovals = 2;
117131
const committeeApprovers = Array.from(approvers)
118132
.filter(approver => requiredApprovers.includes(approver));
119133
const currentCountfromCommittee = committeeApprovers.length;
120134
121-
// Core logic that checks if the approvers are in the committee
122-
const conclusion = (currentCountfromCommittee >= 2) ? 'success' : 'failure';
135+
// Check if we have enough approvals
136+
const conclusion = (currentCountfromCommittee >= requiredApprovals) ? 'success' : 'failure';
123137
124138
console.log('PR Approval Status');
139+
console.log('Modified folders:');
140+
console.log(`- doc/: ${affectsDocs ? 'yes' : 'no'}`);
141+
console.log(`- library/: ${affectsLibrary ? 'yes' : 'no'}`);
142+
console.log(`- verifast-proofs/: ${affectsVerifast ? 'yes' : 'no'}`);
143+
console.log(`Required approvals from committee: ${requiredApprovals}`);
125144
console.log(`PR has ${approvers.size} total approvals and ${currentCountfromCommittee} required approvals from the committee.`);
126145
127146
console.log(`Committee Members: [${requiredApprovers.join(', ')}]`);
128147
console.log(`Committee Approvers: ${committeeApprovers.length === 0 ? 'NONE' : `[${committeeApprovers.join(', ')}]`}`);
129148
console.log(`All Approvers: ${approvers.size === 0 ? 'NONE' : `[${Array.from(approvers).join(', ')}]`}`);
130149
131150
if (conclusion === 'failure') {
132-
core.setFailed(`PR needs 2 approvals from committee members, but it has ${currentCountfromCommittee}`);
151+
core.setFailed(`PR needs ${requiredApprovals} approval${requiredApprovals > 1 ? 's' : ''} from committee members, but it has ${currentCountfromCommittee}`);
133152
} else {
134153
core.info('PR approval check passed successfully.');
135154
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Session.vim
2222
/kani_build/
2323
/target
2424
library/target
25+
scripts/autoharness_analyzer/target/
2526
*.rlib
2627
*.rmeta
2728
*.mir

doc/mdbook-metrics/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ edition = "2021"
55

66
[dependencies]
77
mdbook = { version = "^0.4" }
8+
mdbook-linkcheck = "0.7.7"
89
serde_json = "1.0.132"

doc/src/SUMMARY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@
3232
- [16: Verify the safety of Iterator functions](./challenges/0016-iter.md)
3333
- [17: Verify the safety of slice functions](./challenges/0017-slice.md)
3434
- [18: Verify the safety of slice iter functions](./challenges/0018-slice-iter.md)
35+
- [19: Safety of `RawVec`](./challenges/0019-rawvec.md)
36+
- [20: Verify the safety of char-related functions in str::pattern](./challenges/0020-str-pattern-pt1.md)
37+
- [21: Verify the safety of substring-related functions in str::pattern](./challenges/0021-str-pattern-pt2.md)
38+
- [22: Verify the safety of str iter functions](./challenges/0022-str-iter.md)
39+
- [25: Verify the safety of `VecDeque` functions](./challenges/0025-vecdeque.md)

doc/src/challenges/0007-atomic-types.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,18 @@ Write and verify safety contracts for the unsafe functions:
8787
- `atomic_umax`
8888
- `atomic_umin`
8989

90+
##### Panicking (Optional)
9091
Then, for each of the safe abstractions that invoke the unsafe functions listed above, write contracts that ensure that they are not invoked with `order`s that would cause panics.
9192

9293
For example, `atomic_store` panics if invoked with `Acquire` or `AcqRel` ordering.
9394
In this case, you would write contracts on the safe `store` methods that enforce that they are not called with either of those `order`s.
9495

96+
This section is not required to complete the challenge, since panicking is not undefined behavior.
97+
However, it would be incorrect for someone to call these functions with the wrong arguments, so we encourage providing these specifications.
98+
9599
#### Part 3: Atomic Intrinsics
96100

97-
Write and verify safety contracts for the intrinsics invoked by the unsafe functions from Part 2 (in `core::intrinsics`):
101+
Write safety contracts for the intrinsics invoked by the unsafe functions from Part 2 (in `core::intrinsics`):
98102

99103
| Operations | Functions |
100104
|-----------------------|-------------|

doc/src/challenges/0019-rawvec.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Challenge 25: Verify the safety of `RawVec` functions
2+
3+
- **Status:** Open
4+
- **Tracking Issue:** [#283](https://github.com/model-checking/verify-rust-std/issues/283)
5+
- **Start date:** *2025-03-07*
6+
- **End date:** *2025-10-17*
7+
- **Reward:** *10000 USD*
8+
9+
-------------------
10+
11+
12+
## Goal
13+
14+
Verify the safety of `RawVec` functions in (library/alloc/src/raw_vec/mod.rs).
15+
16+
## Motivation
17+
18+
`RawVec` is the type of the main component of both `Vec` and `VecDeque`: the buffer. Therefore, the safety of the functions of `Vec` and `VecDeque` depend on the safety of `RawVec`.
19+
20+
### Success Criteria
21+
22+
Verify the safety of the following functions in (library/alloc/src/raw_vec/mod.rs):
23+
24+
Write and prove the contract for the safety of the following unsafe functions:
25+
26+
| Function |
27+
|---------|
28+
|new_cap|
29+
|into_box|
30+
|from_raw_parts_in|
31+
|from_nonnull_in|
32+
|set_ptr_and_cap|
33+
|shrink_unchecked|
34+
|deallocate|
35+
36+
Prove the absence of undefined behavior for following safe abstractions:
37+
38+
| Function |
39+
|---------|
40+
|drop|
41+
|new_in|
42+
|with_capacity_in|
43+
|try_allocate_in|
44+
|current_memory|
45+
|try_reserve|
46+
|try_reserve_exact|
47+
|grow_amortized|
48+
|grow_exact|
49+
|shrink|
50+
|finish_grow|
51+
52+
The verification must be unbounded---it must hold for slices of arbitrary length.
53+
54+
The verification must hold for generic type `T` (no monomorphization).
55+
56+
### List of UBs
57+
58+
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):
59+
60+
* Accessing (loading from or storing to) a place that is dangling or based on a misaligned pointer.
61+
* Reading from uninitialized memory except for padding or unions.
62+
* Mutating immutable bytes.
63+
* Producing an invalid value
64+
65+
66+
Note: All solutions to verification challenges need to satisfy the criteria established in the [challenge book](../general-rules.md)
67+
in addition to the ones listed above.

0 commit comments

Comments
 (0)