Home

Awesome

Verifying concurrent, crash-safe systems with Perennial

CI

Perennial is a system for verifying correctness for systems with both concurrency and crash-safety requirements, including recovery procedures. For example, think of file systems, concurrent write-ahead logging like Linux's jbd2 layer, and persistent key-value stores like RocksDB.

Perennial uses Goose to enable verification of programs written in (a subset of) Go.

This repository also includes a proof of correctness for GoJournal, the verified journaling system used in go-nfsd, including a simple NFS server built on top.

Compiling

We develop Perennial using Coq master and maintain compatibility with the latest version of Coq.

This project uses git submodules to include several dependencies. You should run git submodule update --init --recursive to set that up.

To compile just run make with Coq on your $PATH.

We compile with coqc.py, a Python wrapper around coqc to get timing information. The wrapper requires Python3 and the argparse library. You can also compile without timing information with make TIMED=false.

Maintaining dependencies

There are a few dependencies managed as submodules in external/. To update them, run git submodule update --init --remote, then commit the resulting change with git.

The dependencies are frozen at a particular version to avoid breaking the Perennial build when there are incompatible upstream changes. We use Dependabot to do daily checks for dependency updates.

Compilation times

Perennial takes about 120 CPU minutes to compile. Compiling in parallel with make -j4 is significantly faster, and can cut the time down to 45-50 minutes.

Incremental builds are better, after Iris and some core libraries are compiled.

When you make a change to a dependency, you can keep working without fully compiling the dependency by compiling vos interface files, which skips proofs. The simplest way to do this is just to run make vos, but it's fastest to pass a specific target, like make src/program_proof/wal/proof.required_vos, which only builds the vos dependencies to work on the wal/proof.v file.

If you're working on Goose and only need to re-check the Goose output, you can run make interpreter to run the interpreter's semantics tests, or directly compile just the Goose output files.

Coq also has a feature called vok files, where coqc compiles a vos file without requiring its dependencies to be built. The process does not produce a self-contained vo file, but emits an empty vok file to record that checking is complete. This allows checking individual files completely and in parallel. Using vos and vok files can significantly speed up the edit-compile-debug cycle. Note that technically vok checking isn't the same as regular compilation - it doesn't check universe constraints in the same way.

Updating Goose output

This repo has committed versions of the output of Goose, to avoid making Go and Goose a dependency for compilation. You can update these using the ./etc/update-goose.py script, which records exactly how to generate the output for the various Goose projects we have. Use ./etc/update-goose.py --help to get all the options. The script only translates the projects you pass a path to.

Source organization

src/

It's also worth noting that external/Goose contains committed copies of the Goose output on some real code we have. This includes github.com/tchajed/marshal and github.com/mit-pdos/goose-nfsd. The directory structure here mirrors the way Go import paths work.

Publications

Perennial 1 is described in our SOSP paper, "Verifying concurrent, crash-safe systems with Perennial". The actual codebase was quite different at the time of this paper; it notably used a shallow embedding of Goose and did not have WPCs or any of the associated program logic infrastructure. See the tag sosp2019 or the shallow branch.

Goose is briefly described in a CoqPL extended abstract and associated talk, "Verifying concurrent Go code in Coq with Goose".

The verified interpreter and test framework for Goose is described in Sydney Gibson's masters thesis, "Waddle: A proven interpreter and test framework for a subset of the Go semantics".

The proof of GoJournal's correctness is described in the OSDI paper, "GoJournal: a verified, concurrent, crash-safe journaling system". The framework has evolved in several ways since then. See the tag osdi21 for the version used there.