|
1 | 1 |  |
2 | | -[](https://github.com/model-checking/kani/actions/workflows/kani.yml) |
3 | | -[](https://github.com/model-checking/kani/actions/workflows/cbmc-latest.yml) |
4 | 2 |
|
5 | | -The Kani Rust Verifier is a bit-precise model checker for Rust. |
| 3 | +**This is a feature branch of Kani that contains an experimental Boogie-based backend. If you are looking for the main version of Kani, visit https://github.com/model-checking/kani instead.** |
6 | 4 |
|
7 | | -Kani is particularly useful for verifying unsafe code blocks in Rust, where the "[unsafe superpowers](https://doc.rust-lang.org/stable/book/ch19-01-unsafe-rust.html#unsafe-superpowers)" are unchecked by the compiler. |
8 | | -___ |
9 | | -Kani verifies: |
10 | | - * Memory safety (e.g., null pointer dereferences) |
11 | | - * User-specified assertions (i.e., `assert!(...)`) |
12 | | - * The absence of panics (e.g., `unwrap()` on `None` values) |
13 | | - * The absence of some types of unexpected behavior (e.g., arithmetic overflows) |
| 5 | +The main version of Kani translates [MIR](https://blog.rust-lang.org/2016/04/19/MIR.html) to Goto and uses CBMC as the verification engine. |
14 | 6 |
|
15 | | -## Installation |
| 7 | +This branch implements a translation of MIR to [Boogie](https://github.com/boogie-org/boogie) that can be verified using the Boogie Verifier. |
| 8 | +It currently supports a very small subset of Rust. |
16 | 9 |
|
17 | | -To install the latest version of Kani ([Rust 1.58+; Linux or Mac](https://model-checking.github.io/kani/install-guide.html)), run: |
| 10 | +The Boogie backend is not included in Kani's [releases](https://github.com/model-checking/kani/releases), so in order to use it, you need to clone this branch and build from source. |
| 11 | +Refer to the [Installing from source code](https://model-checking.github.io/kani/build-from-source.html) section of the documentation for instructions on how to build Kani from source. |
18 | 12 |
|
| 13 | +## Prerequisites |
| 14 | + |
| 15 | +You need to have the Boogie Verifier installed. |
| 16 | +Refer to https://github.com/boogie-org/boogie#installation for the installation instructions. |
| 17 | + |
| 18 | +## Instructions for Using the Boogie Backend |
| 19 | + |
| 20 | +To invoke the Boogie backend, pass the unstable `-Zboogie` option to the Kani command, e.g. |
| 21 | +``` |
| 22 | +kani test.rs -Zboogie |
| 23 | +``` |
| 24 | +or |
| 25 | +``` |
| 26 | +cargo kani -Zboogie |
| 27 | +``` |
| 28 | +Kani will print the name of the generated Boogie file, e.g. |
| 29 | +``` |
| 30 | +Writing Boogie file to /home/ubuntu/test1_main.symtab.bpl |
| 31 | +``` |
| 32 | +You need to manually invoke the Boogie verifier on the generated Boogie file to verify it: |
19 | 33 | ```bash |
20 | | -cargo install --locked kani-verifier |
21 | | -cargo kani setup |
| 34 | +$ boogie test1_main.symtab.bpl |
| 35 | + |
| 36 | +Boogie program verifier finished with 1 verified, 0 errors |
22 | 37 | ``` |
23 | 38 |
|
24 | | -See [the installation guide](https://model-checking.github.io/kani/install-guide.html) for more details. |
| 39 | +### Example |
25 | 40 |
|
26 | | -## How to use Kani |
| 41 | +Consider the following function (whose source file can be found [here](https://github.com/model-checking/kani/blob/features/boogie/tests/script-based-boogie/unbounded_array_copy/test.rs)) which creates an array of integers, `src`, and copies it into another array of integers, `dst`: |
| 42 | +```rust |
| 43 | +#[kani::proof] |
| 44 | +fn copy() { |
| 45 | + let src = kani::array::any_array::<i32>(); |
| 46 | + let mut dst = kani::array::any_array::<i32>(); |
| 47 | + let src_len: usize = src.len(); |
| 48 | + let dst_len: usize = dst.len(); |
27 | 49 |
|
28 | | -Similar to testing, you write a harness, but with Kani you can check all possible values using `kani::any()`: |
| 50 | + // copy as many elements as possible of `src` to `dst` |
| 51 | + let mut i: usize = 0; |
| 52 | + // Loop invariant: forall j: usize :: j < i => dst[j] == src[j]) |
| 53 | + while i < src_len && i < dst_len { |
| 54 | + dst[i] = src[i]; |
| 55 | + i = i + 1; |
| 56 | + } |
29 | 57 |
|
30 | | -```rust |
31 | | -use my_crate::{function_under_test, meets_specification, precondition}; |
| 58 | + // check that the data was copied |
| 59 | + i = 0; |
| 60 | + while i < src_len && i < dst_len { |
| 61 | + kani::assert(dst[i] == src[i], "element doesn't have the correct value"); |
| 62 | + i = i + 1; |
| 63 | + } |
| 64 | +} |
| 65 | +``` |
| 66 | +The arrays are created using `kani::array::any_array`, which creates an array with non-deterministic content and length. |
32 | 67 |
|
33 | | -#[kani::proof] |
34 | | -fn check_my_property() { |
35 | | - // Create a nondeterministic input |
36 | | - let input = kani::any(); |
| 68 | +Running Kani with `-Zboogie` produces the following Boogie file: |
| 69 | +<details> |
| 70 | + <summary>Click to expand Boogie file contents</summary> |
37 | 71 |
|
38 | | - // Constrain it according to the function's precondition |
39 | | - kani::assume(precondition(input)); |
| 72 | +```boogie |
| 73 | +// Datatypes: |
| 74 | +datatype $Array<T> { $Array(data: [bv64]T, len: bv64) } |
40 | 75 |
|
41 | | - // Call the function under verification |
42 | | - let output = function_under_test(input); |
| 76 | +// Functions: |
| 77 | +function {:bvbuiltin "bvult"} $BvUnsignedLessThan<T>(lhs: T, rhs: T) returns (bool); |
43 | 78 |
|
44 | | - // Check that it meets the specification |
45 | | - assert!(meets_specification(input, output)); |
| 79 | +function {:bvbuiltin "bvslt"} $BvSignedLessThan<T>(lhs: T, rhs: T) returns (bool); |
| 80 | +
|
| 81 | +function {:bvbuiltin "bvugt"} $BvUnsignedGreaterThan<T>(lhs: T, rhs: T) returns (bool); |
| 82 | +
|
| 83 | +function {:bvbuiltin "bvsgt"} $BvSignedGreaterThan<T>(lhs: T, rhs: T) returns (bool); |
| 84 | +
|
| 85 | +function {:bvbuiltin "bvadd"} $BvAdd<T>(lhs: T, rhs: T) returns (T); |
| 86 | +
|
| 87 | +function {:bvbuiltin "bvor"} $BvOr<T>(lhs: T, rhs: T) returns (T); |
| 88 | +
|
| 89 | +function {:bvbuiltin "bvand"} $BvAnd<T>(lhs: T, rhs: T) returns (T); |
| 90 | +
|
| 91 | +function {:bvbuiltin "bvshl"} $BvShl<T>(lhs: T, rhs: T) returns (T); |
| 92 | +
|
| 93 | +function {:bvbuiltin "bvlshr"} $BvShr<T>(lhs: T, rhs: T) returns (T); |
| 94 | +
|
| 95 | +// Procedures: |
| 96 | +procedure _RNvCs7Oe89NXlEjS_4test4copy() |
| 97 | +{ |
| 98 | + var src: $Array bv32; |
| 99 | + var dst: $Array bv32; |
| 100 | + var src_len: bv64; |
| 101 | + var _4: $Array bv32; |
| 102 | + var dst_len: bv64; |
| 103 | + var _6: $Array bv32; |
| 104 | + var i: bv64; |
| 105 | + var _8: bool; |
| 106 | + var _9: bv64; |
| 107 | + var _10: bool; |
| 108 | + var _11: bv64; |
| 109 | + var _12: bv32; |
| 110 | + var _13: bv32; |
| 111 | + var _14: $Array bv32; |
| 112 | + var _15: bv64; |
| 113 | + var _18: bv64; |
| 114 | + var _19: bv64; |
| 115 | + var _20: bv64; |
| 116 | + var _21: bool; |
| 117 | + var _22: bv64; |
| 118 | + var _23: bool; |
| 119 | + var _24: bv64; |
| 120 | + var _26: bool; |
| 121 | + var _27: bv32; |
| 122 | + var _28: bv32; |
| 123 | + var _29: $Array bv32; |
| 124 | + var _30: bv64; |
| 125 | + var _31: bv32; |
| 126 | + var _32: bv32; |
| 127 | + var _33: $Array bv32; |
| 128 | + var _34: bv64; |
| 129 | + var _35: bv64; |
| 130 | + var _36: bv64; |
| 131 | + bb0: |
| 132 | + havoc src; |
| 133 | + goto bb1; |
| 134 | + bb1: |
| 135 | + havoc dst; |
| 136 | + goto bb2; |
| 137 | + bb2: |
| 138 | + src_len := src->len; |
| 139 | + bb3: |
| 140 | + dst_len := dst->len; |
| 141 | + bb4: |
| 142 | + i := 0bv64; |
| 143 | + goto bb5; |
| 144 | + bb5: |
| 145 | + _9 := i; |
| 146 | + _8 := $BvUnsignedLessThan(_9, src_len); |
| 147 | + if ((_8 == false)) { |
| 148 | + goto bb11; |
| 149 | + } else { |
| 150 | + goto bb6; |
| 151 | + } |
| 152 | + bb6: |
| 153 | + _11 := i; |
| 154 | + _10 := $BvUnsignedLessThan(_11, dst_len); |
| 155 | + if ((_10 == false)) { |
| 156 | + goto bb11; |
| 157 | + } else { |
| 158 | + goto bb7; |
| 159 | + } |
| 160 | + bb11: |
| 161 | + i := 0bv64; |
| 162 | + goto bb12; |
| 163 | + bb12: |
| 164 | + _22 := i; |
| 165 | + _21 := $BvUnsignedLessThan(_22, src_len); |
| 166 | + if ((_21 == false)) { |
| 167 | + goto bb19; |
| 168 | + } else { |
| 169 | + goto bb13; |
| 170 | + } |
| 171 | + bb13: |
| 172 | + _24 := i; |
| 173 | + _23 := $BvUnsignedLessThan(_24, dst_len); |
| 174 | + if ((_23 == false)) { |
| 175 | + goto bb19; |
| 176 | + } else { |
| 177 | + goto bb14; |
| 178 | + } |
| 179 | + bb19: |
| 180 | + return; |
| 181 | + bb14: |
| 182 | + _30 := i; |
| 183 | + _28 := dst->data[(_30)]; |
| 184 | + bb15: |
| 185 | + _27 := _28; |
| 186 | + _34 := i; |
| 187 | + _32 := src->data[(_34)]; |
| 188 | + bb16: |
| 189 | + _31 := _32; |
| 190 | + _26 := (_27 == _31); |
| 191 | + assert _26; |
| 192 | + bb17: |
| 193 | + _35 := i; |
| 194 | + _36 := $BvAdd(_35, 1bv64); |
| 195 | + bb18: |
| 196 | + i := _36; |
| 197 | + goto bb12; |
| 198 | + bb7: |
| 199 | + _15 := i; |
| 200 | + _13 := src->data[(_15)]; |
| 201 | + bb8: |
| 202 | + _12 := _13; |
| 203 | + _18 := i; |
| 204 | + bb9: |
| 205 | + dst->data[(_18)] := _12; |
| 206 | + _19 := i; |
| 207 | + _20 := $BvAdd(_19, 1bv64); |
| 208 | + bb10: |
| 209 | + i := _20; |
| 210 | + goto bb5; |
46 | 211 | } |
47 | 212 | ``` |
| 213 | +</details> |
48 | 214 |
|
49 | | -Kani will then try to prove that all valid inputs produce acceptable outputs, without panicking or executing unexpected behavior. |
50 | | -Otherwise Kani will generate a trace that points to the failure. |
51 | | -We recommend following [the tutorial](https://model-checking.github.io/kani/kani-tutorial.html) to learn more about how to use Kani. |
| 215 | +If we invoke the Boogie Verifier on the generated file, it fails to prove the assertion: |
| 216 | +```bash |
| 217 | +$ boogie test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl |
| 218 | +test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(119,3): Error: this assertion could not be proved |
| 219 | +Execution trace: |
| 220 | + test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(59,3): bb0 |
| 221 | + test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(72,3): bb5 |
| 222 | + test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(76,5): anon8_Then |
| 223 | + test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(91,3): bb12 |
| 224 | + test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(97,5): anon10_Else |
| 225 | + test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl(105,5): anon11_Else |
| 226 | + |
| 227 | +Boogie program verifier finished with 0 verified, 1 error |
| 228 | +``` |
| 229 | +This is due to the presence of a loop, which requires specifying a loop invariant. |
| 230 | +If we add the following assertion after the `bb5:` line (which is the loop head): |
| 231 | +``` |
| 232 | +assert (forall j: bv64 :: $BvUnsignedLessThan(j, i) ==> dst->data[j] == src->data[j]); |
| 233 | +``` |
| 234 | +verification succeeds: |
| 235 | +```bash |
| 236 | +$ boogie test__RNvCs7Oe89NXlEjS_4test4copy.symtab.bpl |
| 237 | + |
| 238 | +Boogie program verifier finished with 1 verified, 0 errors |
| 239 | +``` |
52 | 240 |
|
53 | 241 | ## GitHub Action |
54 | 242 |
|
|
0 commit comments