|
| 1 | +# Flux |
| 2 | + |
| 3 | +[Flux](https://flux-rs.github.io/flux) is a refinement type checker |
| 4 | +that can be used to prove user-defined safety properties for Rust. |
| 5 | +Users can write their own properties via refinement type contracts |
| 6 | +(a generalization of pre- and post-conditions). Additionally, out of the |
| 7 | +box, Flux checks for various issues such as |
| 8 | + |
| 9 | +- arithmetic overflows |
| 10 | +- division by zero |
| 11 | +- array bounds violations |
| 12 | + |
| 13 | +## Installation |
| 14 | + |
| 15 | +See the [installation section of the Flux book](https://flux-rs.github.io/flux/install.html) |
| 16 | +to learn how to install and run Flux. |
| 17 | + |
| 18 | + |
| 19 | +## Usage |
| 20 | + |
| 21 | +(Adapted from the Kani documentation; see [the Flux book](https://flux-rs.github.io/flux) |
| 22 | +for many more examples.) |
| 23 | + |
| 24 | +Consider a Rust file `demo.rs` with a function that returns |
| 25 | +the absolute value of the difference between two integers. |
| 26 | +To *prove* that the function always returns a *non-negative* result, you write |
| 27 | +a Flux specification—above the function definition—that says the output |
| 28 | +is a refinement type `i32{v: 0 <= v}` denoting non-negative `i32` values. |
| 29 | + |
| 30 | + |
| 31 | +``` rust |
| 32 | +// demo.rs |
| 33 | +#[allow(dead_code)] |
| 34 | +#[cfg_attr(flux, flux::spec(fn(a: i32, b: i32) -> i32{v:0 <= v}))] |
| 35 | +fn test_abs(a:i32, b:i32) -> i32 { |
| 36 | + if a > b { |
| 37 | + a - b |
| 38 | + } else { |
| 39 | + b - a |
| 40 | + } |
| 41 | +} |
| 42 | +``` |
| 43 | + |
| 44 | +Now, if you run |
| 45 | + |
| 46 | +``` |
| 47 | +$ flux --crate-type=lib demo.rs |
| 48 | +``` |
| 49 | + |
| 50 | +Flux will return immediately with **no output** indicating the code is safe. |
| 51 | + |
| 52 | +This may be baffling for those with a keen understanding of arithmetic overflows: |
| 53 | +what if `a` is `INT_MAX` and `b` is `INT_MIN`? Indeed, Flux has overflow checking |
| 54 | +off by default but you can optionally switch it on for a function, module, or entire crate. |
| 55 | + |
| 56 | +``` rust |
| 57 | +// demo.rs |
| 58 | +#[allow(dead_code)] |
| 59 | +#[cfg_attr(flux, flux::opts(check_overflow = true))] |
| 60 | +#[cfg_attr(flux, flux::spec(fn(a: i32, b: i32) -> i32{v:0 <= v}))] |
| 61 | +fn test_abs(a:i32, b:i32) -> i32 { |
| 62 | + if a > b { |
| 63 | + a - b |
| 64 | + } else { |
| 65 | + b - a |
| 66 | + } |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +Now, when you run `flux` you see that |
| 71 | + |
| 72 | +``` |
| 73 | +$ flux --crate-type=lib demo.rs |
| 74 | +error[E0999]: arithmetic operation may overflow |
| 75 | + --> demo.rs:9:9 |
| 76 | + | |
| 77 | +9 | b - a |
| 78 | + | ^^^^^ |
| 79 | +
|
| 80 | +error[E0999]: arithmetic operation may overflow |
| 81 | + --> demo.rs:7:9 |
| 82 | + | |
| 83 | +7 | a - b |
| 84 | + | ^^^^^ |
| 85 | +
|
| 86 | +error: aborting due to 2 previous errors |
| 87 | +``` |
| 88 | + |
| 89 | +You might *fix* the errors, i.e., prove the function have no overflows, |
| 90 | +by requiring the *inputs* also be non-negative: |
| 91 | + |
| 92 | +```rust |
| 93 | +#[allow(dead_code)] |
| 94 | +#[cfg_attr(flux, flux::opts(check_overflow = true))] |
| 95 | +#[cfg_attr(flux, flux::spec(fn(a: i32{0 <= a}, b: i32{0 <= b}) -> i32{v: 0 <= v}))] |
| 96 | +fn test_abs(a:u32, b:u32) -> u32 { |
| 97 | + if a > b { |
| 98 | + a - b |
| 99 | + } else { |
| 100 | + b - a |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +This time `flux --crate-type=lib demo.rs` finishes swiftly without reporting any errors. |
| 106 | + |
| 107 | +For a more detailed online interactive tutorial, with many more examples, see [the Flux book](https://flux-rs.github.io/flux). |
| 108 | + |
| 109 | +## Using Flux to verify the (model-checking fork of) the Rust Standard Library |
| 110 | + |
| 111 | +Currrently, we have configured Flux to run on the |
| 112 | +[model-checking fork](https://github.com/model-checking/verify-rust-std) |
| 113 | +of the Rust Standard Library. You can run Flux on |
| 114 | + |
| 115 | +1. a single function, |
| 116 | +2. a single file, |
| 117 | +3. a set of files matching a glob-pattern, or |
| 118 | +4. the entire crate. |
| 119 | + |
| 120 | +### Running on a Single Function |
| 121 | + |
| 122 | +Suppose the function is in a file inside `library/core/src/`, e.g., |
| 123 | +`library/core/src/ascii/ascii_char.rs`. |
| 124 | +For example, let's suppose `test_abs` was added to the end of that file, |
| 125 | +*with* the `check_overflow = true` and *without* the `flux::spec` contract. |
| 126 | +Then if you did |
| 127 | + |
| 128 | +```bash |
| 129 | +$ cd library/core |
| 130 | +$ FLUXFLAGS="-Fcheck-def=test_abs" cargo flux |
| 131 | +``` |
| 132 | + |
| 133 | +you should get some output like |
| 134 | + |
| 135 | +``` |
| 136 | + Checking core v0.0.0 (/verify-rust-std/library/core) |
| 137 | +
|
| 138 | +error[E0999]: arithmetic operation may overflow |
| 139 | + --> core/src/ascii/ascii_char.rs:635:9 |
| 140 | + | |
| 141 | +635 | b - a |
| 142 | + | ^^^^^ |
| 143 | +
|
| 144 | +error[E0999]: arithmetic operation may overflow |
| 145 | + --> core/src/ascii/ascii_char.rs:633:9 |
| 146 | + | |
| 147 | +633 | a - b |
| 148 | + | ^^^^^ |
| 149 | +
|
| 150 | +error: could not compile `core` (lib) due to 2 previous errors |
| 151 | +
|
| 152 | +Checking core v0.0.0 (/verify-rust-std/library/core) |
| 153 | +Finished `flux` profile [unoptimized + debuginfo] target(s) in 5.54s |
| 154 | +``` |
| 155 | + |
| 156 | + |
| 157 | +You can now fix the error by adding the non-negative input specification above the definition |
| 158 | +of `test_abs` |
| 159 | + |
| 160 | +```rust |
| 161 | +#[cfg_attr(flux, flux::spec(fn(a: i32{0 <= a}, b: i32{0 <= b}) -> i32{v: 0 <= v})] |
| 162 | +``` |
| 163 | + |
| 164 | +and when you re-run, it should finish with no warnings |
| 165 | + |
| 166 | +**NOTE** When checking inside `core`, we wrap the `flux` specification attributes |
| 167 | +in `#[cfg_attr(flux,...)]` so they are only read by flux. |
| 168 | + |
| 169 | +### Running on a Single File |
| 170 | + |
| 171 | +To run on a single _file_ you can just pass the name of that file to flux (relative from the |
| 172 | +crate root) as |
| 173 | + |
| 174 | +```bash |
| 175 | +$ cd library/core |
| 176 | +$ FLUXFLAGS="-Finclude=src/ascii/ascii_char.rs" cargo flux |
| 177 | +``` |
| 178 | + |
| 179 | +### Running on a Globset of Files |
| 180 | + |
| 181 | +To run on a comma-separated _globset of files_, e.g., an entire module, you can just pass |
| 182 | +the appropriate globset as |
| 183 | + |
| 184 | +```bash |
| 185 | +$ cd library/core |
| 186 | +$ FLUXFLAGS="-Finclude=src/ascii/*" cargo flux |
| 187 | +``` |
| 188 | + |
| 189 | +### Configuring Flux via `Cargo.toml` |
| 190 | + |
| 191 | +Flux can also be configured in the `Cargo.toml` under the |
| 192 | +`[package.metadata.flux]` section. |
| 193 | + |
| 194 | +You can add or remove things from the `include` declaration there |
| 195 | +to check other files. Currently, it is configured to only run on a |
| 196 | +tiny subset of the modules; you should comment out that line if you |
| 197 | +want to run on _all_ of `core`. |
| 198 | + |
| 199 | +You can run flux on all the parts of the `core` crate specified in `include` |
| 200 | +directive in the `Cargo.toml` by doing |
| 201 | + |
| 202 | +```bash |
| 203 | +$ cd library/core |
| 204 | +$ cargo flux |
| 205 | +``` |
| 206 | + |
| 207 | +### Running on the Entire Crate |
| 208 | + |
| 209 | +Currently the `core/Cargo.toml` is configured (see the `[package.metadata.flux]`) to |
| 210 | +only run on a tiny subset of the modules, you should comment out that line if you |
| 211 | +really want to run on _all_ of `core`, and then run |
| 212 | + |
| 213 | +```bash |
| 214 | +$ cd library/core |
| 215 | +$ cargo flux |
| 216 | +``` |
| 217 | + |
| 218 | +Sadly, various Rust features exercised by the crate are not currently supported by |
| 219 | +Flux making it crash ignominously. |
| 220 | + |
| 221 | +You can tell it to do a "best-effort" analysis by ignoring those crashes |
| 222 | +(as much as possible) by |
| 223 | + |
| 224 | +```bash |
| 225 | +$ FLUXFLAGS="-Fcatch-bugs" cargo flux |
| 226 | +``` |
| 227 | + |
| 228 | +in which case Flux will grind over the whole crate and point out nearly 300+ *possible* |
| 229 | +warnings about overflows, array bounds accesses etc., |
| 230 | +and also about 200+ ICE (places where it crashes!) To do this, you may also have |
| 231 | +to tell Flux to not check certain modules, e.g. by adding |
| 232 | +various `flux::trusted` annotations [as shown here](https://github.com/flux-rs/verify-rust-std/blob/fluxable/library/core/src/lib.rs) |
| 233 | + |
| 234 | + |
| 235 | +## More details |
| 236 | + |
| 237 | +You can find more information on using Flux—especially on how to write different |
| 238 | +kinds of specifications and configuration options—in [the Flux book](https://flux-rs.github.io/flux). |
| 239 | +Happy proving! |
0 commit comments