| Crates.io | secrust |
| lib.rs | secrust |
| version | 0.1.0-alpha.3 |
| created_at | 2025-01-05 06:02:03.459895+00 |
| updated_at | 2025-01-05 23:02:37.093899+00 |
| description | A Rust crate for source-level verification using Weakest Precondition calculus. |
| homepage | |
| repository | https://github.com/vasilevlaicu/secrust |
| max_upload_size | |
| id | 1504512 |
| size | 118,865 |
Secrust is a Rust crate designed to add formal verification to Rust code. By adding lightweight annotations to Rust functions, Secrust enables developers to verify their methods with annotated invariants, preconditions, and postconditions directly in their source code.
Secrust leverages Rust's syntax and ecosystem, integrating with the language's tooling to provide an intuitive developer experience. The crate uses the Z3 SMT solver to reason about program correctness and generates Control Flow Graphs (CFGs) to visualize execution paths, making it easier to identify and eliminate logical errors.
Secrust currently supports simple Rust code:
if/else branches to ensure correctness across all execution paths.Install Z3 using Homebrew:
brew install z3
brew info z3
Set environment variables to point to the Z3 paths (modify as needed for your setup):
export Z3_SYS_Z3_HEADER=/opt/homebrew/Cellar/z3/4.13.3/include/z3.h
export Z3_SYS_Z3_LIB_DIR=/opt/homebrew/Cellar/z3/4.13.3/lib
export LIBRARY_PATH=/opt/homebrew/Cellar/z3/4.13.3/lib:$LIBRARY_PATH
export LD_LIBRARY_PATH=/opt/homebrew/Cellar/z3/4.13.3/lib:$LD_LIBRARY_PATH
Install secrust pre-release using Cargo:
cargo install secrust --version 0.1.0-alpha.3
Download Z3:
C:\z3.
bin folder: Includes z3.exe and .dll files.include folder: Contains header files like z3.h.Set Environment Variables:
Z3_SYS_Z3_HEADER=C:\z3\include\z3.h
Z3_SYS_Z3_LIB_DIR=C:\z3\bin
LIBRARY_PATH=C:\z3\bin
LD_LIBRARY_PATH=C:\z3\bin
C:\z3\bin to your PATH.Ensure You Have GCC Installed:
pacman -Syu
pacman -S mingw-w64-x86_64-toolchain
PATH:
C:\msys64\mingw64\bin
Ensure You Have LLVM Installed:
bin directory of LLVM (e.g., C:\LLVM\bin) to your PATH.LIBCLANG_PATH environment variable:
LIBCLANG_PATH=C:\LLVM\bin
Install secrust using Cargo:
cargo install secrust --version 0.1.0-alpha.3
Or if you download the repo:
cargo install --path ..\secrust
Run the following command to ensure secrust is installed correctly:
cargo secrust-verify --help
Analyze a file without generating Control Flow Graphs:
cargo secrust-verify main.rs
Analyze a file and generate DOT files for the Control Flow Graph:
cargo secrust-verify src/main.rs --dot
DOT files are created in the src/graphs/filename directory for the specified file (e.g., src/main.rs).
You can visualize DOT code online on edotor.net
sum_first_nThe following example demonstrates how to verify a simple Rust function using secrust.
Save the following code as src/main.rs, and make sure to annotate it with pre!, invariant! and post! assertions:
use secrust::{build_cfg, invariant, old, post, pre};
fn sum_first_n(n: i32) -> i32 {
pre!(n >= 0);
let mut sum = 0;
let mut i = 1;
invariant!(i <= n + 1 && sum == (i - 1) * i / 2);
while i <= n {
sum = sum + i;
i = i + 1;
}
post!(sum == n * (n + 1) / 2);
return sum;
}
fn main() {
let n = 5;
let sum = sum_first_n(n);
println!("Sum is: {}", sum);
}
Run the secrust verification on this file:
cargo secrust-verify src/main.rs --dot
src/graphs/main directory.For example:
main.dot will contain the CFG for the main function.basic_path_0.dot will contain the graph for the first basic execution path of the annotated sum_first_n function.To generate a DOT format CFG for any method without adding logical annotations, add the build_cfg!(); macro at the start of the method.
Use tools like Graphviz to visualize the DOT files:
dot -Tpng src/graphs/main/basic_path_0.dot -o basic_path_0.png
Or paste the DOT code on an online editor like edotor.net.
sum_first_nTo showcase how Secrust works, let’s walk through the verification process for a simple Rust function that calculates the sum of the first ( n ) integers.
pre! and post! annotate the function with input and output conditions.invariant! provides the loop invariant, which must hold before and after each iteration of while.When you run Secrust with the --dot flag:
cargo secrust-verify src/main.rs --dot
Secrust parses the AST to build a Control Flow Graph (CFG). Then it extracts basic paths, each corresponding to a distinct route through the function.
Figure: CFG generated by Secrust that was manually highlighted to show all basic paths extracted by Secrust and Path 3 basic path representation. Secrust will also save each basic path in a separate .DOT file
Among the paths generated, let’s focus on Path 3, the route taken when the while condition i <= n is true.
Secrust analyzes each basic path by traversing it backward from the postcondition, repeatedly applying WP rules.
Start from the loop invariant at the “bottom” of the path:
i <= n + 1 AND sum == (i - 1) * i / 2
Move upward through assignments like:
sum = sum + i;
i = i + 1;
which update sum to sum + i and i to i + 1. Secrust substitutes these into the invariant, yielding:
(i + 1) <= n + 1 AND (sum + i) == ((i + 1) - 1) * (i + 1) / 2
Encounter the while i <= n (true branch). This adds an assumption:
(i <= n) => ((i + 1) <= n + 1 AND (sum + i) == ((i + 1) - 1) * (i + 1) / 2)
Finally, we link it back to the loop’s starting invariant (the path’s precondition).
Hence, the final logical implication for Path 3 is:
(i <= n + 1 AND sum == (i - 1) * i / 2) => (i <= n) => ((i + 1) <= n + 1 AND (sum + i) == ((i + 1) - 1) * (i + 1) / 2))
After deriving this implication, Secrust:
Below is a simplified example of the final formula in SMT-LIB:
(=> (and (<= i (+ n 1)) (= sum (div (* (- i 1) i) 2)))
(=> (<= i n)
(and (<= (+ i 1) (+ n 1))
(= (+ sum i) (div (* (- (+ i 1) 1) (+ i 1)) 2)))))
Because the solver reports unsatisfiable for its negation, Path 3 is verified. Repeating this process for all basic paths ensures the entire function satisfies its preconditions, invariants, and postconditions.
sum_first_n with pre!, post!, and invariant!.Licensed under either of: