You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
File: "./src/bounds_check.rs", line 11, in bounds_check::get_wrapped
98
+
99
+
VERIFICATION:- FAILED
81
100
```
82
101
83
-
The first is Rust's implicit assertion for the safe indexing operation.
102
+
The first is Rust's runtime bounds checking for the safe indexing operation.
84
103
The second is Kani's check to ensure the pointer operation is actually safe.
85
-
This pattern (two checks for similar issues in safe Rust code) is common, and we'll see it again in the next section.
104
+
This pattern (two checks for similar issues in safe Rust code) is common to see, and we'll see it again in the next section.
105
+
106
+
> **NOTE**: While Kani will always be checking for both properties, [in the future the output here may change](https://github.com/model-checking/kani/issues/1349).
107
+
> You might have noticed that the bad pointer dereference can't happen, since the bounds check would panic first.
108
+
> In the future, Kani's output may report only the bounds checking failure in this example.
86
109
87
110
</details>
88
111
89
112
<details>
90
113
<summary>Click to see explanation for exercise 2</summary>
91
114
92
-
Having run `kani --visualize` and clicked on one of the failures to see a trace, there are three things to immediately notice:
115
+
Having run `cargo kani --harness bound_check --visualize` and clicked on one of the failures to see a trace, there are three things to immediately notice:
93
116
94
117
1. This trace is huge. Because the standard library `Vec` is involved, there's a lot going on.
95
118
2. The top of the trace file contains some "trace navigation tips" that might be helpful in navigating the trace.
96
119
3. There's a lot of generated code and it's really hard to just read the trace itself.
97
120
98
-
To navigate this trace to find the information you need, we recommend searching for things you expect to be somewhere in the trace:
121
+
To navigate this trace to find the information you need, we again recommend searching for things you expect to be somewhere in the trace:
99
122
100
-
1. Search the document for `kani::any` or `<variable_of_interest> =` such as `size =`.
123
+
1. Search the page for `kani::any` or `<variable_of_interest> =` such as `size =` or `let size`.
101
124
We can use this to find out what example values lead to a problem.
102
125
In this case, where we just have a couple of `kani::any` values in our proof harness, we can learn a lot just by seeing what these are.
103
126
In this trace we find (and the values you get may be different):
104
127
105
128
```
106
-
Step 23: Function bound_check, File src/bounds_check.rs, Line 43
107
-
let size: usize = kani::any();
108
-
size = 0ul
109
-
110
-
Step 27: Function bound_check, File src/bounds_check.rs, Line 45
111
-
let index: usize = kani::any();
112
-
index = 0ul
113
-
114
129
Step 36: Function bound_check, File src/bounds_check.rs, Line 43
115
130
let size: usize = kani::any();
116
131
size = 2464ul
@@ -120,13 +135,13 @@ let index: usize = kani::any();
120
135
index = 2463ul
121
136
```
122
137
123
-
Try not to be fooled by the first assignments: we're seeing zero-initialization there.
124
-
Their values are overwritten by the later assignments.
125
138
You may see different values here, as it depends on the solver's behavior.
126
139
127
-
2. Try searching for "failure:". This will be near the end of the document.
128
-
Now you can try reverse-searching for assignments to the variables involved.
129
-
For example, search upwards from the failure for `i =`.
140
+
2. Try searching for `failure:`. This will be near the end of the page.
141
+
You can now search upwards from a failure to see what values certain variables had.
142
+
Sometimes it can be helpful to change the source code to add intermediate variables, so their value is visible in the trace.
143
+
For instance, you might want to compute the index before indexing into the array.
144
+
That way you'd see in the trace exactly what value is being used.
130
145
131
146
These two techniques should help you find both the nondeterministic inputs, and the values that were involved in the failing assertion.
We've corrected the out-of-bounds access, but now we've omitted the "base case": what to return on an empty list.
146
161
Kani will spot this not as a bound error, but as a mathematical error: on an empty list the modulus operator (`%`) will cause a division by zero.
147
162
148
-
1. Exercise: Try to run Kani on the above, to see what this kind of failure looks like.
163
+
1. Exercise: Try to run Kani on this version of `get_wrapped`, to see what this kind of failure looks like.
149
164
150
-
Rust also performs runtime safety checks for integer overflows, much like it does for bounds checks.
165
+
Rust can also perform runtime safety checks for integer overflows, much like it does for bounds checks.
166
+
([Though Rust disables this by default in `--release` mode, it can be re-enabled.](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow))
151
167
Consider this code (available [here](https://github.com/model-checking/kani/blob/main/docs/src/tutorial/kinds-of-failure/src/overflow.rs)):
152
168
153
169
```rust
@@ -159,28 +175,33 @@ Kani will find these failures as well.
159
175
Here's the output from Kani:
160
176
161
177
```
162
-
# kani src/overflow.rs --harness add_overflow
163
-
[...]
164
-
RESULTS:
165
-
Check 1: simple_addition.assertion.1
166
-
- Status: FAILURE
167
-
- Description: "attempt to add with overflow"
178
+
# cargo kani --harness add_overflow
168
179
[...]
180
+
SUMMARY:
181
+
** 1 of 2 failed
182
+
Failed Checks: attempt to add with overflow
183
+
File: "./src/overflow.rs", line 7, in overflow::simple_addition
184
+
169
185
VERIFICATION:- FAILED
170
186
```
171
187
172
188
This issue can be fixed using Rust's alternative mathematical functions with explicit overflow behavior.
173
-
For instance, instead of `a + b` write `a.wrapping_add(b)`.
189
+
For instance, if the wrapping behavior is intended, you can write `a.wrapping_add(b)` instead of `a + b`.
190
+
Kani will then report no issues.
174
191
175
192
### Exercise: Classic overflow failure
176
193
177
-
One of the classic subtle bugs that persisted in many implementations for a very long time is finding the midpoint in quick sort.
194
+
A classic example of a subtle bug that persisted in many implementations for a very long time is "finding the midpoint" in quick sort.
178
195
This often naively looks like this (code available [here](https://github.com/model-checking/kani/blob/main/docs/src/tutorial/kinds-of-failure/src/overflow_quicksort.rs)):
But if you naively try this (try it!), you'll find a new underflow error: `high - low` might result in a negative number, but has type `u32`.
201
-
Hence, the need to add an assumption that would make that impossible.
202
-
(Adding an assumption, though, means there's a new way to "use it wrong." Perhaps we'd like to avoid that!)
222
+
Hence, the need to add the assumption we suggested above, to make that impossible.
223
+
(Adding an assumption, though, means there's a new way to "use it wrong." Perhaps we'd like to avoid that! Can you avoid the assumption?)
203
224
204
225
After that, you might wonder how to "prove your new implementation correct."
205
226
After all, what does "correct" even mean?
206
227
Often we're using a good approximation of correct, such as the equivalence of two implementations (often one much "simpler" than the other somehow).
207
-
Here's one possible assertion to make that obvious:
228
+
Here's one possible assertion we could write in the proof harness:
208
229
209
230
```rust
210
231
assert!(resultasu64== (aasu64+basu64) /2);
211
232
```
212
233
213
-
Since this implementation is just the original one, but cast to a wider unsigned integer type, it should have the same result but without overflowing.
214
-
When Kani tells us both of these methods yield the same exact result, that gives us additional confidence that we haven't overlooked something.
234
+
You might have even come up with this approach to avoiding the overflow issue in the first place!
235
+
Having two different implementations, using different approaches, but proven to yield the same results, gives us greater confidence that we compute the correct result.
215
236
216
237
</details>
217
238
218
239
## Failures that Kani cannot spot
219
240
220
-
Check out [Limitations](./limitations.md) for information on the checks that
221
-
Kani doesn't perform.
241
+
Check out [Limitations](./limitations.md) for information on the checks that Kani does not perform.
242
+
Notably, Kani is not prioritizing all Rust-specific notions of undefined behavior.
243
+
244
+
## Summary
245
+
246
+
In this section:
247
+
248
+
1. We saw Kani spot out-of-bounds accesses.
249
+
2. We saw Kani spot actually-unsafe dereferencing of a raw pointer to invalid memory.
250
+
3. We got more experience reading the traces that Kani generates, to debug a failing proof harness.
251
+
3. We saw Kani spot a division by zero error and an overflowing addition.
252
+
5. As an exercise, we tried proving an assertion (finding the midpoint) that was not completely trivial.
0 commit comments