This document provides guidance for AI coding assistants (Kiro, Claude Code, Copilot, etc.) when working with the Kani codebase.
Kani is a bit-precise model checker for Rust. It verifies both safety and correctness of Rust code by:
- Safety: Automatically checking for undefined behavior, making it particularly useful for verifying
unsafecode blocks - Correctness: Checking for panics (e.g.,
unwrap()onNone), arithmetic overflows, and custom correctness properties via assertions or function contracts
Kani uses CBMC as its underlying verification engine to provide bit-precise verification of Rust programs.
| Directory | Description |
|---|---|
kani-compiler/ |
Rust compiler plugin that translates Rust MIR to CBMC's goto-program format |
kani-driver/ |
CLI driver that orchestrates the verification workflow (invokes compiler, CBMC, processes results) |
cprover_bindings/ |
Rust bindings for CBMC's internal representation (goto-program, symbols, types) |
kani_metadata/ |
Data structures for metadata shared between compiler and driver |
| Directory | Description |
|---|---|
library/kani/ |
Main Kani library providing the verification API (kani::any(), kani::assume(), etc.) |
library/kani_core/ |
Core library with fundamental Kani definitions |
library/kani_macros/ |
Procedural macros (#[kani::proof], #[kani::requires], etc.) |
library/std/ |
Standard library overrides for verification |
| Directory | Description |
|---|---|
tests/ |
Comprehensive test suites (see Testing section) |
scripts/ |
Build, test, and CI scripts |
tools/ |
Supporting tools (compiletest, benchcomp, kani-cov, etc.) |
| Directory | Description |
|---|---|
docs/ |
User and developer documentation (built with mdBook) |
rfc/ |
Request for Comments documents for design decisions |
papers/ |
Academic papers related to Kani |
# Development build (debug mode)
cargo build-dev
# Release build (with optimizations)
cargo build-dev -- --release
# To build Kani with both the default CPROVER and the experimental LLBC back-end
# enabled, use:
cargo build-dev -- --features cprover --features llbc
# Clean build artifacts if encountering stale cache issues
cargo clean
cargo build-dev# Full regression suite
./scripts/kani-regression.sh
# To run the LLBC-specific regression tests:
./scripts/kani-llbc-regression.sh
# Run a specific test suite
cargo run -p compiletest -- --suite kani --mode kani
cargo run -p compiletest -- --suite expected --mode expected
cargo run -p compiletest -- --suite cargo-kani --mode cargo-kani
# Unit tests for specific packages
cargo test -p cprover_bindings
cargo test -p kani-compiler
cargo test -p kani-driver
cargo test -p kani_metadata
cargo test -p kani --features concrete_playback# Verify a single Rust file
kani file.rs
# Verify a Cargo project
cargo kani
# Useful debugging flags
kani --debug file.rs # Enable debug logging
kani --keep-temps file.rs # Keep intermediate files
kani --gen-c file.rs # Generate C code from CBMC IR
KANI_LOG="kani_compiler::kani_middle=trace" kani file.rs # Fine-grained logging# Check formatting
./scripts/kani-fmt.sh --check
# Auto-format code
./scripts/kani-fmt.sh| Suite | Mode | Description |
|---|---|---|
kani |
kani | Main test suite; single Rust files verified with Kani |
expected |
expected | Tests with expected output verification (.expected files) |
ui |
expected | User interface tests (warnings, error messages) |
cargo-kani |
cargo-kani | Tests for cargo kani command with full Cargo projects |
cargo-ui |
cargo-kani | Cargo-based UI tests |
firecracker |
kani | Tests inspired by Firecracker codebase |
prusti |
kani | Tests from Prusti verifier |
smack |
kani | Tests from SMACK verifier |
kani-fixme |
kani-fixme | Known failing tests (marked with fixme or ignore) |
script-based-pre |
exec | Script-based tests with custom setup |
coverage |
coverage-based | Coverage verification tests |
Single-file tests (in tests/kani/):
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT
// kani-flags: --default-unwind 4
// (optional command-line flags)
#[kani::proof]
fn check_something() {
let x: u32 = kani::any();
kani::assume(x < 100);
assert!(x * 2 < 200);
}Expected output tests (in tests/expected/):
- Create a
.rsfile with the test - Create a corresponding
.expectedfile with expected output patterns
"Fixme" tests: Name files with fixme or ignore to mark known-failing tests that demonstrate bugs or unsupported features.
- Use descriptive names that indicate what's being tested
- Include issue numbers in test file names or comments when applicable
- For regression tests, include the GitHub issue number (e.g.,
issue_1234.rs)
Proof harnesses are the entry points for verification, similar to test functions:
#[kani::proof]
fn check_property() {
// Create nondeterministic inputs
let input: u8 = kani::any();
// Add assumptions to constrain inputs
kani::assume(input > 0);
// Call the function under verification
let result = function_under_test(input);
// Check properties with assertions
assert!(result > 0);
}| Function/Macro | Purpose |
|---|---|
kani::any() |
Generate nondeterministic value of any type implementing Arbitrary |
kani::assume(cond) |
Restrict verification to paths where cond is true |
assert!(cond) |
Verify that cond holds on all paths |
kani::cover!(cond) |
Check if cond is reachable |
Contracts allow modular verification by specifying function behavior:
#[kani::requires(x > 0)] // Precondition
#[kani::ensures(|result| *result > x)] // Postcondition
fn increment(x: u32) -> u32 {
x + 1
}
// Verify the contract
#[kani::proof_for_contract(increment)]
fn check_increment() {
let x: u32 = kani::any();
increment(x);
}
// Use verified contract as a stub
#[kani::proof]
#[kani::stub_verified(increment)]
fn check_caller() {
let result = increment(5);
assert!(result == 6);
}| Attribute | Description |
|---|---|
#[kani::proof] |
Marks a function as a proof harness |
#[kani::unwind(N)] |
Set loop unwinding bound |
#[kani::should_panic] |
Expect the harness to panic |
#[kani::solver(solver)] |
Specify SAT solver (minisat, cadical, kissat, z3, etc.) |
#[kani::stub(orig, repl)] |
Replace function with stub |
#[kani::requires(cond)] |
Function precondition |
#[kani::ensures(cond)] |
Function postcondition |
#[kani::modifies(ptr)] |
Specify modified memory |
#[kani::proof_for_contract(fn)] |
Verify a function's contract |
#[kani::stub_verified(fn)] |
Use verified contract as stub |
- Follow the existing code style (enforced by
rustfmtwithrustfmt.toml) - Use Clippy lints (some are allowed, see
.cargo/config.toml) - C code uses
clang-format, Python usesautopep8
All new source files must begin with:
// Copyright Kani Contributors
// SPDX-License-Identifier: Apache-2.0 OR MIT- Soundness over convenience: Kani must never produce false negatives (saying code is safe when it isn't)
- Clear error messages: Follow rustc diagnostic guidelines
- Explicit over implicit: Use
#[allow]annotations locally rather than disabling lints globally - Document TODOs: Create GitHub issues for TODO items and reference them in comments
- Sort match arms, enum variants, and struct fields alphabetically
- Prefer exhaustive matches over wildcard patterns
- Prefer declarative over imperative programming
- Keep names concise but meaningful
- Verify if the bug still exists by creating a regression test using the reported code sample
- If the bug no longer reproduces, add the test case and create a PR that resolves the issue
- Check if there's an existing RFC or issue discussing the feature
- For significant changes, open an issue first to discuss the approach
- Add comprehensive tests covering the new functionality
- Update documentation as needed
- Work against the latest
mainbranch - Focus on one specific change per PR
- Ensure local tests pass (
./scripts/kani-regression.sh) - Use clear commit messages (PRs are squash-merged)
- Respond to CI failures and review feedback