Skip to content
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

Add VeriFast CI #239

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Add VeriFast CI #239

wants to merge 3 commits into from

Conversation

btj
Copy link

@btj btj commented Jan 21, 2025

Adds a GitHub Actions workflow that downloads VeriFast and runs verifast-proofs/check-verifast-proofs.mysh. (mysh is a simple tool that ships with VeriFast for running simple scripts. It is optimized for running test suites (and used to run VeriFast's test suite. Works on Linux, Mac, and Windows.)

See #238 to see how this can be used to verify the linked_list.rs (Challenge 5) solution.

Resolves #213

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

@btj btj requested a review from a team as a code owner January 21, 2025 18:36
@remi-delmas-3000 remi-delmas-3000 self-requested a review January 22, 2025 04:09
@feliperodri feliperodri added the Tool Application Used to tag tool application label Jan 22, 2025
Copy link

@remi-delmas-3000 remi-delmas-3000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@btj do you think you could add a simple proof example in ./verifast-proofs and implement the diff and tool invocation logic in check-verifast-proofs.mysh ? We'd like to be able to check that the action runs/fails/succeeds at least once. A positive and a negative example would be great


unsafe fn reverse(mut n: *mut Node) -> *mut Node
//@ req Nodes(n, ?nodes);
//@ ens Nodes(result, reverse(nodes));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to use a different name for the mathematical list reverse function and the rust reverse method ? This could be confusing for some novice users (myself included). By the way does ghost code live in a separate namespace ? I'm guessing not since you can refer to Rust function parameters in the spec. How does overloading work between Rust code and verifast ghost code for function symbols ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Real functions and lemmas share the same namespace. No two functions can have the same name. But in this example the real function is called Node::reverse and the lemma is called reverse, so there's no clash.

}
```
A lemma is like a regular Rust function, except that it is declared inside an annotation. VeriFast checks that it has no side effects and that it terminates.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we say that the general methodology/mental model for proving some Rust code implementing some data structure would be to: first, formalize the data structure layout, its invariants and functions in in Verifast's own separation logic language, and then prove that the Rust code and the abstract specification compute the same thing by adding annotations explaining how both compute in lockstep (you are essentially weaving the spec and the program's executions together) ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not quite how I think of it. The specification (the req clause and the ens clause) is not computational/imperative; it's declarative. The lemmas are imperative but they are proofs of their specification; I think of them as mathematical lemmas stating that the precondition implies the postcondition.

```
The Rust compiler accepts it just fine, but VeriFast complains that it cannot prove that when this function returns, the ownership of the value pointed to by `dest` is *separate* from the ownership of the return value. If we replace the final line by `result`, VeriFast accepts the code.

For a function not marked as `unsafe`, VeriFast generates a specification expressing that the function is *semantically well-typed* per [RustBelt](https://research.ralfj.de/thesis.html)'s definition of what Rust's types mean in separation logic. If no specification clause annotations are provided for the function, VeriFast verifies the function against the generated specification; otherwise, VeriFast first verifies that the provided specification implies the generated one, and then verifies the function against the provided specification.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precision the "generated specification" is the default specification derived automatically by Verifast, according to RustBelts separation logic model of rust types, from the function signature.


For a function not marked as `unsafe`, VeriFast generates a specification expressing that the function is *semantically well-typed* per [RustBelt](https://research.ralfj.de/thesis.html)'s definition of what Rust's types mean in separation logic. If no specification clause annotations are provided for the function, VeriFast verifies the function against the generated specification; otherwise, VeriFast first verifies that the provided specification implies the generated one, and then verifies the function against the provided specification.

The generated specification for `replace` is as follows:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we call this the default specification ? Or automatically generated ?

//@ ens thread_token(_t) &*& *dest |-> ?dest1 &*& <T>.own(_t, dest1) &*& <T>.own(_t, result);
```
`<T>.own(t, v)` expresses ownership of the T value `v` accessible to thread `t` (in case T is not [Send](https://doc.rust-lang.org/nomicon/send-and-sync.html)).
`thread_token(t)` represents permission to open *nonatomic invariants* and *nonatomic borrows* at thread `t`; these contain resources shared in a non-thread-safe way at thread `t`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the concept of opening predicates has been explained so far in this doc, a link to a definition would be useful.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence is not about opening predicates, it's about opening invariants and borrows; I wanted to briefly motivate thread_token but I'm not sure it makes sense to try and explain those concepts here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add a README file to this folder that describes the intended puprpose and organisation guidelines ?
From side discussions it seems that the folder will contain copies of standard library files extended with verifast ghost code, and that check-verifast-proofs.mysh will run all the diff logic and verifast analyses, but this needs to be explained here

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would really be awsome to have an actual example of a working proof as part of this PR too, so that we can check the action is working.

@btj
Copy link
Author

btj commented Feb 5, 2025

I'm developing a "refinement checker", a simple tool that checks that one .rs file F1 is a "refinement" of another one F2. This means that for every function f in F1, and for every execution of F1, function f in F2 has a matching execution. It follows that if F2 has been verified to have no "bad"/unsafe executions, F1 also has no bad executions. This tool should support the kinds of changes that I have made in linked_list.rs. If this works, this solves the diff problem: there will no longer be a need for maintainers to manually check diffs for "soundness". Instead, it will suffice to run this tool on the original file and the verified file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Tool Application Used to tag tool application
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add tool: VeriFast
3 participants