Verifying concurrent Go code in Coq with Goose Tej Chajed , Joseph - - PowerPoint PPT Presentation

verifying concurrent go code in coq with goose
SMART_READER_LITE
LIVE PREVIEW

Verifying concurrent Go code in Coq with Goose Tej Chajed , Joseph - - PowerPoint PPT Presentation

Verifying concurrent Go code in Coq with Goose Tej Chajed , Joseph Tassarotti*, Frans Kaashoek, Nickolai Zeldovich MIT and *Boston College Systems verification, broadly impl proof specification 2 Systems verification requires connecting


slide-1
SLIDE 1

Verifying concurrent Go code in Coq with Goose

Tej Chajed, Joseph Tassarotti*, Frans Kaashoek, Nickolai Zeldovich

MIT and *Boston College

slide-2
SLIDE 2

2

Systems verification, broadly

impl specification proof

slide-3
SLIDE 3

3

Systems verification requires connecting implementation to proof

impl proof model of impl specification

slide-4
SLIDE 4

3

Systems verification requires connecting implementation to proof

impl proof

this talk

model of impl

previous [SOSP 2019] and current work

specification

slide-5
SLIDE 5

4

We aim to verify realistic systems

Systems: running code, interacts with outside world Realistic: reasonably efficient, concurrency Verification: functional correctness, focus on crash safety

PDOS (the part that does verification)

slide-6
SLIDE 6

5

Goal: implement in a systems language

impl model of impl

(1) write ordinary imperative code (3) prove something about the model (2) import into Coq

slide-7
SLIDE 7

6

Goose: write code in Go and prove with Iris

Why Go (vs. C or Rust)? Simple, good tooling Why Iris (vs. VST)? Concurrency, extensibility

slide-8
SLIDE 8

7

Implementing in Go helps build the software

impl model of impl

(1a) write ordinary imperative code (3) prove something about the model (2) import into Coq (1b) test (1c) debug (1d) profile (1e) benchmark

slide-9
SLIDE 9

8

Goose: import subset of Go into a Coq model

Goose Go

✓locks ✓fork ✓structs ✓pointers ✗ interfaces

slide-10
SLIDE 10

8

Goose: import subset of Go into a Coq model

Goose Go goose translator

✓locks ✓fork ✓structs ✓pointers ✗ interfaces

Coq

slide-11
SLIDE 11

8

Goose: import subset of Go into a Coq model

Goose Go GooseLang goose translator λref,conc with
 external ops

✓locks ✓fork ✓structs ✓pointers ✗ interfaces

Coq

slide-12
SLIDE 12

8

Goose: import subset of Go into a Coq model

Goose Go GooseLang goose translator λref,conc with
 external ops

✓locks ✓fork ✓structs ✓pointers ✗ interfaces

Coq carry out proofs in Iris

slide-13
SLIDE 13

9

Our systems verification research using Goose

Persistent key-value store using file system (unverified) Mail server using file system (appeared in SOSP ’19) Concurrent file system using disk (in progress)

slide-14
SLIDE 14

10

Go is a systems language

C-like: functions, structs, pointers Exposes system calls Efficient runtime (garbage collection, threads)

slide-15
SLIDE 15

11

Goose code

Looks like standard Go, but avoids most of the standard library Use narrow interfaces for file system or disk More of Go is supported frequently

slide-16
SLIDE 16

12

Challenges in implementing Goose

Defining GooseLang, a semantic model of Go Translating Go to GooseLang

slide-17
SLIDE 17

13

GooseLang, a semantic model of Go

e ::= x | λx. e | e1 e2 // λ-calculus
 | ref e | !e | e1 ← e2 // heap operations
 | fork e | cmpxchg // concurrency | call op e // external operations

slide-18
SLIDE 18

13

GooseLang, a semantic model of Go

e ::= x | λx. e | e1 e2 // λ-calculus
 | ref e | !e | e1 ← e2 // heap operations
 | fork e | cmpxchg // concurrency | call op e // external operations

slide-19
SLIDE 19

13

GooseLang, a semantic model of Go

e ::= x | λx. e | e1 e2 // λ-calculus
 | ref e | !e | e1 ← e2 // heap operations
 | fork e | cmpxchg // concurrency | call op e // external operations

v ::= U64 x | Loc z | … // literals | Pair | InjL | InjR // sums, products

slide-20
SLIDE 20

14

Excerpt from GooseLang:
 slices

ptr len x = (ptr, len) …

Definition sliceAppend := λ s, x. let s’ := alloc (s.len + #1) () in … (* fill s’ *) (s’, s.len + #1).

Coq

slide-21
SLIDE 21

14

Excerpt from GooseLang:
 slices

func example(x []uint64) { x1 := x[1] append(x, 5) } Definition example := λ x. let x1 := !(x.ptr +ₗ #1) in sliceAppend x #5;; #().

ptr len x = (ptr, len) …

Definition sliceAppend := λ s, x. let s’ := alloc (s.len + #1) () in … (* fill s’ *) (s’, s.len + #1).

goose

Go Coq

slide-22
SLIDE 22

15

Excerpt from GooseLang:
 modeling concurrency and locking

func coin() bool { m := new(sync.Mutex) x := new(bool) go func() { m.Lock() *x = true m.Unlock() }() m.Lock() v := *x m.Unlock() return v

slide-23
SLIDE 23

15

Excerpt from GooseLang:
 modeling concurrency and locking

Definition coin: val := λ <>. let: “m” := lock.new #() in let: “x” := ref #(zero_val boolT) in fork (lock.acquire “m”;; “x” ← #true;; lock.release “m”);; lock.acquire “m”;; let: “v” := !”x” in lock.release “m”;; “v”. func coin() bool { m := new(sync.Mutex) x := new(bool) go func() { m.Lock() *x = true m.Unlock() }() m.Lock() v := *x m.Unlock() return v

goose

slide-24
SLIDE 24

16

Excerpt from GooseLang:
 modeling concurrency and locking

Definition coin: val := λ <>. let: “m” := lock.new #() in let: “x” := ref #(zero_val boolT) in fork (lock.acquire “m”;; “x” ← #true;; lock.release “m”);; lock.acquire “m”;; let: “v” := !”x” in lock.release “m”;; “v”. func coin() bool { m := new(sync.Mutex) x := new(bool) go func() { m.Lock() *x = true m.Unlock() }() m.Lock() v := *x m.Unlock() return v

goose

slide-25
SLIDE 25

17

Excerpt from GooseLang:
 modeling concurrency and locking

Definition coin: val := λ <>. let: “m” := lock.new #() in let: “x” := ref #(zero_val boolT) in fork (lock.acquire “m”;; “x” ← #true;; lock.release “m”);; lock.acquire “m”;; let: “v” := !”x” in lock.release “m”;; “v”. func coin() bool { m := new(sync.Mutex) x := new(bool) go func() { m.Lock() *x = true m.Unlock() }() m.Lock() v := *x m.Unlock() return v

goose

slide-26
SLIDE 26

18

Excerpt from GooseLang:
 modeling concurrency and locking

Definition coin: val := λ <>. let: “m” := lock.new #() in let: “x” := ref #(zero_val boolT) in fork (lock.acquire “m”;; “x” ← #true;; lock.release “m”);; lock.acquire “m”;; let: “v” := !”x” in lock.release “m”;; “v”. func coin() bool { m := new(sync.Mutex) x := new(bool) go func() { m.Lock() *x = true m.Unlock() }() m.Lock() v := *x m.Unlock() return v

goose

slide-27
SLIDE 27

19

Challenge in modeling Go: weak memory

func uhOh(x *uint64) { go func() { *x = 1 print(“set x”) }() print(“x=”, *x) } Definition uhOh: val := λ x. fork (x ← #1 print “set x” !x);; print “x=” !x.

x86-TSO Sequential consistency

goose

If we first see “set x”, then

slide-28
SLIDE 28

19

Challenge in modeling Go: weak memory

func uhOh(x *uint64) { go func() { *x = 1 print(“set x”) }() print(“x=”, *x) } Definition uhOh: val := λ x. fork (x ← #1 print “set x” !x);; print “x=” !x.

x86-TSO Sequential consistency

goose

If we first see “set x”, then sequential consistency means x=1 ✓

slide-29
SLIDE 29

19

Challenge in modeling Go: weak memory

func uhOh(x *uint64) { go func() { *x = 1 print(“set x”) }() print(“x=”, *x) } Definition uhOh: val := λ x. fork (x ← #1 print “set x” !x);; print “x=” !x.

x86-TSO Sequential consistency

goose

If we first see “set x”, then sequential consistency means x=1 but TSO allows x=0 ✗ ✓

slide-30
SLIDE 30

19

Challenge in modeling Go: weak memory

func uhOh(x *uint64) { go func() { *x = 1 print(“set x”) }() print(“x=”, *x) } Definition uhOh: val := λ x. fork (x ← #1 print “set x” !x);; print “x=” !x.

x86-TSO Sequential consistency

goose

If we first see “set x”, then sequential consistency means x=1 but TSO allows x=0 ✗ ✓

slide-31
SLIDE 31

20

Disallow racy loads and stores

Track in-progress stores Concurrent store/store and load/store are undefined

Definition Store: val := λ p, v. BeginStore p;; FinishStore p v. Notation “p ← v” := (Store p v).

slide-32
SLIDE 32

21

Compatibility with Iris gives us amazing verification technology

Concurrent separation logic with higher-order ghost state Iris Proof Mode (IPM) for interactive proofs

slide-33
SLIDE 33

21

Compatibility with Iris gives us amazing verification technology

Concurrent separation logic with higher-order ghost state Iris Proof Mode (IPM) for interactive proofs Connect to our unwritten POPL 2021 paper for crash safety

slide-34
SLIDE 34

22

Proofs using non-atomic memory

{p ↦ v} !p {λv . p ↦ v} {p ↦ v0} p ← v {p ↦ v}

These triples are sound because is exclusive access to

p ↦ v p

Load (non-atomic) Store

slide-35
SLIDE 35

22

Proofs using non-atomic memory

{p ↦ v} !p {λv . p ↦ v} {p ↦ v0} p ← v {p ↦ v}

These triples are sound because is exclusive access to

p ↦ v p

exclude using locks exclude by using local variables Load (non-atomic) Store

slide-36
SLIDE 36

23

GooseLang programs can make system calls

Import disk. Definition Copy: val := λ_. let b := call ReadOp #0 in call WriteOp (#1, b). import “github.com/tchajed/goose/ machine/disk" func Copy() { b := disk.Read(0) disk.Write(1, b) }

Language is parameterized by external calls Currently implementing GooseLang + file-system ops in terms of GooseLang + disk ops

goose

slide-37
SLIDE 37

24

Semantics of GooseLang

Small-step operational semantics, mostly standard and following design of HeapLang For testing, have executable semantics (interpreter + soundness proof)

slide-38
SLIDE 38

25

Previous approach: shallow embedding as semantic model

GooseLang was a free monad instead of a λ-calculus Go code had to explicitly sequence effectful operations Pure operations were expressed directly in Gallina

slide-39
SLIDE 39

26

GooseLang is a mix of shallow and deep embedding

Heap operations, concurrency are deeply represented Data structures are shallowly built out of sums

slide-40
SLIDE 40

27

Goose translator

2.5k lines of Go Implemented using go/ast and go/types Single pass, per function

slide-41
SLIDE 41

28

Goose translator supports enough Go

multiple return values early return for loops slice and map iteration panic struct field pointers struct literals slice element pointers sub-slicing pointers to local variables mutexes and cond vars goroutines ++ and += uint64, uint32, bytes bitwise ops

slide-42
SLIDE 42

29

Goose supports more of Go whenever
 Frans and Nickolai need something

my advisors

✓Multiple packages ✓First-class functions ✓Interfaces and type casts

slide-43
SLIDE 43

29

Goose supports more of Go whenever
 Frans and Nickolai need something

my advisors

✓Multiple packages ✓First-class functions ✓Interfaces and type casts ✗ Channels

slide-44
SLIDE 44

29

Goose supports more of Go whenever
 Frans and Nickolai need something

my advisors

✓Multiple packages ✓First-class functions ✓Interfaces and type casts ✗ Channels ✗ Control flow like return from loop, defer

slide-45
SLIDE 45

30

Making the goose translator sound

Simple and syntactic translation Make mistakes result in undefined behavior Basic type checking catches many mistakes Hand-audited integration tests

slide-46
SLIDE 46

31

Related work

Extraction VST and CompCert RustBelt

slide-47
SLIDE 47

32

Ongoing work

Scaling Goose: handling a large, efficient program Testing: using executable semantics to test translator

slide-48
SLIDE 48

33

Conclusion

Goose is a new approach to concurrent systems verification: imports Go into Coq Actively using it for current research Come talk to us!

Goose Go GooseLang

goose

Tej and Joe are at CoqPL

https://github.com/tchajed/goose