generated from amazon-archives/__template_Apache-2.0
-
Notifications
You must be signed in to change notification settings - Fork 129
Kani Book Documentation Improvements #4296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
1d8000b
list subcommand documentation
c4d7da5
update regression testing docs
95575b8
document experimental support for uninit memory
2f521fd
add other kani attributes
111be7c
add note about reference section to tutorial
f17c98b
document the Arbitrary trait
eed98f1
add chapter about debugging slow proofs
108a3f6
Update docs/src/reference/list.md
tautschnig da61b2c
Update docs/src/regression-testing.md
tautschnig 3a9c5fa
Update docs/src/reference/attributes.md
tautschnig ef681ba
Update docs/src/reference/arbitrary.md
tautschnig 7e44d20
Update docs/src/reference/arbitrary.md
tautschnig f2c11ff
Update docs/src/debugging-slow-proofs.md
tautschnig e3bbdc4
Update docs/src/debugging-slow-proofs.md
tautschnig File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # Debugging Slow Proofs | ||
|
|
||
| Kani uses SAT/SMT solvers to verify code, which can sometimes result in slow or non-terminating proofs. This chapter outlines common causes of slowness and strategies to debug and improve proof performance. | ||
|
|
||
| ## Common Causes of Slow Proofs | ||
|
|
||
| ### Complex/Large Non-deterministic Types | ||
| Some types are inherently more expensive to represent symbolically, e.g. strings, which have complex validation rules for UTF-8 encoding, | ||
| or large bounded collections, like a vector with a large size. | ||
|
|
||
| ### Large Value Operations | ||
| Mathematical operations on large values can be expensive, e.g., multiplication/division/modulo, especially with larger types (e.g., `u64`). | ||
|
|
||
| ### Unbounded Loops | ||
| If Kani cannot determine a loop bound, it will unwind forever, c.f. [the loop unwinding tutorial](./tutorial-loop-unwinding.md). | ||
|
|
||
| ## Debugging Strategies | ||
|
|
||
| These are some strategies to debug slow proofs, ordered roughly in terms of in the order you should try them: | ||
|
|
||
| ### Limit Loop Iterations | ||
|
|
||
| First, identify whether (unbounded) loop unwinding may be the root cause. Try the `#[kani::unwind]` attribute or the `--unwind` option to limit [loop unwinding](./tutorial-loop-unwinding.md). If the proof fails because the unwind value is too low, but raising it causing the proof to be too slow, try specifying a [loop contract](./reference/experimental/loop-contracts.md) instead. | ||
|
|
||
| ### Use Different Solvers | ||
|
|
||
| Kani supports multiple SAT/SMT solvers that may perform differently on your specific problem. Try out different solvers with the `#[kani::solver]` [attribute](./reference/attributes.md) or `--solver` option. | ||
|
|
||
| ### Remove Sources of Nondeterminism | ||
|
|
||
| Start by replacing `kani::any()` calls with concrete values to isolate the problem: | ||
|
|
||
| ```rust | ||
| #[kani::proof] | ||
| fn slow_proof() { | ||
| // Instead of this: | ||
| // let x: u64 = kani::any(); | ||
| // let y: u64 = kani::any(); | ||
|
|
||
| // Try this: | ||
| let x: u64 = 42; | ||
| let y: u64 = 100; | ||
|
|
||
| let result = complex_function(x, y); | ||
| assert!(result > 0); | ||
| } | ||
| ``` | ||
|
|
||
| If the proof becomes fast with concrete values, the issue is likely with the symbolic representation of your inputs. In that case, see you can [partition the proof](#partition-the-input-space) to cover different ranges of possible values, or restrict the proof to a smaller range of values if that is acceptable for your use case. | ||
|
|
||
| ### Reduce Collection Sizes | ||
|
|
||
| Similarly, if smaller values are acceptable for your proof, use those instead: | ||
|
|
||
| ```rust | ||
| #[kani::proof] | ||
| fn test_with_small_collection() { | ||
| // Instead of a large Vec | ||
| // let vec: Vec<u8> = kani::bounded_any::<_, 100>(); | ||
|
|
||
| // Start with a small size | ||
| let vec: Vec<u8> = kani::bounded_any::<_, 2>(); | ||
|
|
||
| process_collection(&vec); | ||
| } | ||
| ``` | ||
|
|
||
| ### Partition the Input Space | ||
|
|
||
| Break down complex proofs by partitioning the input space: | ||
|
|
||
| ```rust | ||
| // Instead of one slow proof with large inputs | ||
| #[kani::proof] | ||
| fn test_multiplication_slow() { | ||
| let x: u64 = kani::any(); | ||
| let y: u64 = kani::any(); | ||
|
|
||
| // This might be too slow for the solver | ||
| let result = x.saturating_mul(y); | ||
| assert!(result >= x || x == 0); | ||
| } | ||
|
|
||
| // Split into multiple proofs with bounded inputs | ||
| #[kani::proof] | ||
| fn test_multiplication_small_values() { | ||
| let x: u64 = kani::any_where(|x| *x <= 100); | ||
| let y: u64 = kani::any_where(|y| *y <= 100); | ||
|
|
||
| let result = x.saturating_mul(y); | ||
| assert!(result >= x || x == 0); | ||
| } | ||
|
|
||
| // Insert harnesses for other ranges of `x` and `y` | ||
| ``` | ||
|
|
||
| See this [tracking issue](https://github.com/model-checking/kani/issues/3006) for adding support for such partitioning automatically. | ||
|
|
||
| ### Use Stubs | ||
|
|
||
| If a function has a complex body, consider using a [stub](./reference/experimental/stubbing.md) or a [verified stub](./reference/experimental/contracts.md) to stub the body with a simpler abstraction. | ||
|
|
||
| ### Disable Unnecessary Checks | ||
|
|
||
| If you're focusing on functional correctness rather than safety, you may disable memory safety checks (run `kani --help` for a list of options to do so). Note that disabling these checks may cause Kani to miss undefined behavior, so use it with caution. | ||
|
|
||
| Alternatively, to assume that all assertions succeed and only focus on finding safety violations, use the `--prove-safety-only` option. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| # Arbitrary Trait | ||
|
|
||
| The `Arbitrary` trait is the foundation for generating non-deterministic values in Kani proof harnesses. It allows you to create symbolic values that represent all possible values of a given type. | ||
|
|
||
| For a type to implement `Arbitrary`, Kani must be able to represent every possible value of it, so unbounded types cannot implement it. For nondeterministic representations of unbounded types, e.g., `Vec`, see the [`BoundedArbitrary` trait](./bounded_arbitrary.md). | ||
|
|
||
| ## Overview | ||
|
|
||
| The `Arbitrary` trait defines methods for generating arbitrary (nondeterministic) values: | ||
|
|
||
| ```rust | ||
| pub trait Arbitrary | ||
| where | ||
| Self: Sized, | ||
| { | ||
| fn any() -> Self; | ||
| fn any_array<const MAX_ARRAY_LENGTH: usize>() -> [Self; MAX_ARRAY_LENGTH] { | ||
| [(); MAX_ARRAY_LENGTH].map(|_| Self::any()) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Basic Usage | ||
|
|
||
| Use `kani::any()` to generate arbitrary values in proof harnesses: | ||
|
|
||
| ```rust | ||
| #[kani::proof] | ||
| fn verify_function() { | ||
| let x: u32 = kani::any(); | ||
| let y: bool = kani::any(); | ||
| let z: [char; 10] = kani::any(); | ||
|
|
||
| // x represents all possible u32 values | ||
| // y represents both true and false | ||
| // z represents an array of length 10 where each element can hold all possible char values | ||
| my_function(x, y, z); | ||
| } | ||
| ``` | ||
|
|
||
| Kani implements `Arbitrary` for primitive types and some standard library types. See the [crate trait documentation](https://model-checking.github.io/kani/crates/doc/kani/trait.Arbitrary.html#foreign-impls) for a full list of implementations. | ||
|
|
||
| ## Constrained Values | ||
|
|
||
| Use `any_where` or `kani::assume()` to add constraints to arbitrary values: | ||
|
|
||
| ```rust | ||
| #[kani::proof] | ||
| fn verify_with_constraints() { | ||
| let x: u32 = kani::any_where(|t| *t < 1000); // Constrain x to be less than 1000 | ||
| kani::assume(x % 2 == 0); // Constrain x to be even | ||
|
|
||
| // Now x represents all even numbers from 0 to 998 | ||
| my_function(x); | ||
| } | ||
|
|
||
| ## Derive Implementations | ||
|
|
||
| Kani can automatically derive `Arbitrary` implementations for structs and enums when all their fields/variants implement `Arbitrary`: | ||
|
|
||
| ### Structs | ||
|
|
||
| ```rust | ||
| #[derive(kani::Arbitrary)] | ||
| struct Point { | ||
| x: i32, | ||
| y: i32, | ||
| } | ||
|
|
||
| #[kani::proof] | ||
| fn verify_point() { | ||
| let point: Point = kani::any(); | ||
| // point.x and point.y can be any i32 values | ||
| process_point(point); | ||
| } | ||
| ``` | ||
|
|
||
| ### Enums | ||
|
|
||
| ```rust | ||
| #[derive(kani::Arbitrary)] | ||
| enum Status { | ||
| Ready, | ||
| Processing(u32), | ||
| Error { code: (char, i32) }, | ||
| } | ||
|
|
||
| #[kani::proof] | ||
| fn verify_status() { | ||
| let status: Status = kani::any(); | ||
| // `status` can be any of the variants | ||
| match status { | ||
| Status::Ready => { /* ... */ } | ||
| Status::Processing(id) => { /* id can be any u32 */ } | ||
| Status::Error { code } => { /* code can be any (char, i32) tuple */ } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Manual Implementations | ||
|
|
||
| Implement `Arbitrary` manually when you need constraints or custom logic. For example, Kani [manually implements `Arbitrary` for `NonZero` types](https://github.com/model-checking/kani/blob/100857e99d7506992c4589332a0d7d8dae1ee29a/library/kani_core/src/arbitrary.rs#L48-L60) to exclude zero values, e.g: | ||
|
|
||
| ```rust | ||
| impl Arbitrary for NonZeroU8 { | ||
| fn any() -> Self { | ||
| let val = u8::any(); | ||
| kani::assume(val != 0); | ||
| unsafe { NonZeroU8::new_unchecked(val) } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
tautschnig marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| An alternative means to add value constraints is provided by the [Invariant trait](https://model-checking.github.io/kani/crates/doc/kani/invariant/trait.Invariant.html). | ||
|
|
||
| ## See Also | ||
|
|
||
| - [Nondeterministic Variables Tutorial](../tutorial-nondeterministic-variables.md) | ||
| - [Bounded Non-deterministic Variables](./bounded_arbitrary.md) | ||
| - [First Steps Tutorial](../tutorial-first-steps.md) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # List Command | ||
|
|
||
| The `list` subcommand provides an overview of harnesses and contracts in the provided project/file. This is useful for understanding which parts of your codebase have verification coverage and tracking verification progress. | ||
|
|
||
| ## Usage | ||
| For basic usage, run `cargo kani list` or `kani list <FILE>`. The current options are: | ||
| - `--format [pretty|markdown|json]`: Choose output format | ||
| - `--std`: List harnesses and contracts in the standard library (standalone `kani` only) | ||
|
|
||
| The default format is `pretty`, which prints a table to the terminal, e.g: | ||
|
|
||
| ``` | ||
| Kani Rust Verifier 0.65.0 (standalone) | ||
|
|
||
| Contracts: | ||
| +-------+----------+-------------------------------+----------------------------------------------------------------+ | ||
| | | Crate | Function | Contract Harnesses (#[kani::proof_for_contract]) | | ||
| +=================================================================================================================+ | ||
| | | my_crate | example::implementation::bar | example::verify::check_bar | | ||
| |-------+----------+-------------------------------+----------------------------------------------------------------| | ||
| | | my_crate | example::implementation::foo | example::verify::check_foo_u32, example::verify::check_foo_u64 | | ||
| |-------+----------+-------------------------------+----------------------------------------------------------------| | ||
| | | my_crate | example::implementation::func | example::verify::check_func | | ||
| |-------+----------+-------------------------------+----------------------------------------------------------------| | ||
| | | my_crate | example::prep::parse | NONE | | ||
| |-------+----------+-------------------------------+----------------------------------------------------------------| | ||
| | Total | | 4 | 4 | | ||
| +-------+----------+-------------------------------+----------------------------------------------------------------+ | ||
|
|
||
| Standard Harnesses (#[kani::proof]): | ||
| +-------+----------+-------------------------------+ | ||
| | | Crate | Harness | | ||
| +==================================================+ | ||
| | | my_crate | example::verify::check_modify | | ||
| |-------+----------+-------------------------------| | ||
| | | my_crate | example::verify::check_new | | ||
| |-------+----------+-------------------------------| | ||
| | Total | | 2 | | ||
| +-------+----------+-------------------------------+ | ||
| ``` | ||
|
|
||
| The "Contracts" table shows functions that have contract attributes (`#[requires]`, `#[ensures]`, or `modifies`), and which harnesses exist for those functions. | ||
| The "Standard Harnesses" table lists all of the `#[kani::proof]` harnesses found. | ||
|
|
||
| The `markdown` and `json` options write the same information to Markdown or JSON files, respectively. | ||
|
|
||
| For `--std`, ensure that the provided path points to a local copy of the standard library, e.g. `kani list --std rust/library`. (Compiling the standard library [works differently](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std) than compiling a normal Rust project, hence the separate option). | ||
|
|
||
| For a full list of options, run `kani list --help`. | ||
|
|
||
| ## Autoharness | ||
| Note that by default, this subcommand only detects manual harnesses. The [experimental autoharness feature](./experimental/autoharness.md) accepts a `--list` argument, which runs this subcommand for both manual harnesses and automatically generated harnesses. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.