towards full verification of concurrent libraries
play

Towards full verification of concurrent libraries Viktor Vafeiadis - PowerPoint PPT Presentation

Towards full verification of concurrent libraries Viktor Vafeiadis Program verification Programs considered: Small (<100 LOC) Medium (KLOC) Large (MLOC) Simple


  1. Towards full verification of concurrent libraries Viktor Vafeiadis

  2. Program verification  Programs considered: Small (<100 LOC) Medium (KLOC) Large (MLOC) Simple Complicated  Properties proved: functional correctness Basic safety Full correctness no memory no crashes termination leaks  Proof automation: Manual Semi-automatic Automatic

  3. Concurrent libraries? Small (<100 LOC) Medium (KLOC) Large (MLOC) Simple Complicated  Imperative data structures ... dynamically allocated  Highly concurrent ... shared memory concurrency ... with intended race conditions  Simple interfaces (stacks, queues, finite maps)  No higher-order functions linearizability Basic safety Full correctness no memory no crashes leaks lock-freedom

  4. My program verification work deny-guarantee linearizability correctness PPoPP’06 ESOP’09 CAV’10 CONCUR’07 POPL’10 VMCAI’09 ECOOP’10 RGSep POPL’09 lock-freedom program logics case studies p. techniques VMCAI’10 tools SAS’07 APLAS’09 safety manual semi-automatic automatic

  5. Michael & Scott non-blocking queue head tail null X 1 2 3 4 typedef struct Node_s *Node; struct Queue_s *Q; struct Node_s { init() { int val; n = new Node(); Node next; n → next = null; } Q = new Queue(); Q → head = node; struct Queue_s { Q → tail = node; Node head; } Node tail; }

  6. A slight complication... The tail pointer can lag behind by one node: head tail null X 1 2 3 Except when the queue is empty: head tail null Y X

  7. Enqueue & dequeue Don’t read the code ! dequeue () { enqueue(v) { while (true) { m = new Node(); m → val = v; h = Q → head; m → next = null; t = Q → tail; while (true) { n = h → next; t = Q → tail; if (Q → tail ≠ t) continue ; n = tail → next; if (h == t) { if (Q → tail ≠ tail) continue ; if (n == null) if (n == null) { return EMPTY; if (CAS(&t → next,n,m)) CAS(&Q → tail,t,n); break; } else { } else { if (CAS(&Q → head,h,n)) CAS(&Q → tail,t,n); return n → val; } } } } CAS(&Q → tail,t,n); } }

  8. Length (first attempt) length() { num = 0; curr = Q → head → next; while (curr ≠ null) { num++; curr = curr → next; } return num; } head tail null X 1 2 3 4

  9. Length (second attempt) length() { num = 0; while (true) { t = Q → tail; n = tail → next; Read Q → tail, and ensure if (n == null) break ; that Q → tail → next == null CAS(&Q → tail,t,n); } curr = Q → head → next; while (curr ≠ null) { num++; if (curr == t) break; curr = curr → next; } return num; }

  10. Length (third attempt) length() { num = 0; do { h = Q → head; while (true) { Get a snapshot of t = Q → tail; Q → head and Q → tail n = tail → next; and ensure that if (n == null) break ; Q → tail → next==null. CAS(&Q → tail,t,n); } } while (h ≠ Q → head); curr = h → next; while (curr ≠ null) { num++; if (curr == t) break; curr = curr → next; } return num; }

  11. Verification challenge Functional correctness: Every method executes ‘atomically’ and obeys a high-level specification VMCAI ’09 CAV ’10 Liveness properties, e.g. lock-freedom: At all times, some outstanding method call is guaranteed to terminate. POPL ’09 But first, do shape analysis to: (1) Find data structure invariants VMCAI ’10 (2) Prove memory safety

  12. RGSep Combining rely-guarantee and separation logic

  13. Whence RGSep? Separation logic [Reynolds, O’Hearn ~’01]:  Good at describing heap-allocated data structures  Local reasoning: proofs mention only the footprint . head tail null ∃ h,t. Q ↦ head:h,tail:t ∗ lseg(h,t) ∗ t ↦ next:null Rely-guarantee [Jones, ’83]:  Good at reasoning about concurrency  Describes interference between threads: how the state evolves

  14. Local & shared assertions Logically divide the state into:  Local: only one thread can access it  Shared: any thread can access it. Example: enqueue just before the loop: local state shared state head tail null null RGSep assertions: p, q ::= (P local ¦ P shared ) normal separation logic | p 1 ∨ p 2 assertions (about the local & shared state respectively) | ∃ x. p

  15. Rely-guarantee specifications Rely: interference caused by environment; Describes how the environment is allowed to change the shared precondition state R,G ⊢ RGSep {p} cmd {q} Guarantee: postcondition interference caused by the program. Describes how the program is allowed to change the shared state.

  16. RGSep actions (pre-/postcondition pairs)  Summarize the shared state updates head tail head tail Enqueue null null A B A head tail head tail Dequeue A B A B head tail head tail Advance tail pointer A B A B

  17. The actions of enqueue & dequeue enqueue(v) { dequeue () { m = new Node(); while (true) { m → val = v; Local h = Q → head; m → next = null; updates t = Q → tail; while (true) { n = h → next; t = Q → tail; if (Q → tail ≠ t) continue ; n = tail → next; if (h == t) { if (Q → tail ≠ tail) continue ; if (n == null) if (n == null) { return EMPTY; if (CAS(&t → next,n,m)) ENQUEUE CAS(&Q → tail,t,n); ADV. TAIL break; } else { } else { if (CAS(&Q → head,h,n)) DEQUEUE CAS(&Q → tail,t,n); ADV. TAIL return n → val; } } } } } CAS(&Q → tail,t,n); ADV. TAIL }

  18. The semantics of actions precondition postcondition [[P ↝ Q]] ≝ {(s 1 , s 2 ) | ∃ I ,s,s ′ ,s ctx . s 1 =s ⊎ s ctx ∧ [[P]] I (s) ∧ s 2 =s ′ ⊎ s ctx ∧ [[Q]] I (s ′ ) } R & G are sets of such actions: [[{a 1 ,...a n }]] ≝ ([[a 1 ]] ∪ ... ∪ [[a n ]])*

  19. The action inference problem  Given cmd, R, p: find G, q s.t. R, G ⊢ RGSep {p} cmd {q}. Preferably, the strongest G and q  Top-level programs: R = ∅ and p = true.  Libraries; the most general client: init(); ‖ ? while (?) { if (?) enqueue(?) else if (?) dequeue() else length() }

  20. Experiments No join With lossless join Algorithm Iter Actions Time (s) Iter Actions Time (s) Treiber stack 4 5 0.1 4 2 0.1 M&S two-lock queue 5 26 0.3 5 12 0.3 M&S non-blocking queue 5 10 1.7 5 6 1.5 DGLM non-blocking queue 5 12 2.2 5 8 2.0 Lock-coupling list 4 21 1.0 3 10 0.8 Optimistic list 5 30 109.1 3 10 52.3 Lazy list 5 48 60.0 4 13 26.2 Vechev’s CAS list 3 9 24.7 3 5 8.8 Vechev’s DCAS list 2 6 0.3 3 4 0.3 Run action inference, finding data structure invariants & proving memory safety. Iter: number of iterations for finding the rely-guarantee specs of each thread. Actions: number of actions inferred.

  21. Linearizability Automatically proving linearizability [Vafeiadis, CAV’09]

  22. Linearizability & forward simulation Linearizability: The implementation (of every method) is a refinement of an atomic specification. Standard proof technique: forward simulation Abstract (spec) S abs S’ abs Concrete (impl) S conc S’ conc

  23. Linearization points The implementation is a refinement of an atomic specification. abstract execution concrete execution linearization point (LP)

  24. Linearization point of enqueue enqueue(v) { m = new Node(); m → val = v; m → next = null; while (true) { t = Q → tail; n = tail → next; if (Q → tail ≠ tail) continue ; if (n == null) { Lin. Point if (CAS(&t → next,n,m)) break; (provided CAS succeeds) } else { CAS(&Q → tail,t,n); } } CAS(&Q → tail,t,n); }

  25. Proof search for the LP ?  For each execution path of each method, choose a candidate LP  Check whether it is a valid LP Does this work ?

  26. Proof search for the LP ?  For each execution path of each method, choose a candidate LP  Check whether it is a valid LP Does this work ? Not quite. 1. LPs can be conditional 2. LPs can be in the code of another thread

  27. LP of dequeue, when it returns EMPTY dequeue () { while (true) { h = Q → head; t = Q → tail; n = h → next; LP provided if (Q → tail ≠ t) this test fails, and continue ; the h==t test succeeds if (h == t) { the n==null test succeeds if (n == null) return EMPTY; CAS(&Q → tail,t,n); } else { if (CAS(&Q → head,h,n)) Condition: return n → val; } ¬prophecy(Q → tail ≠ t) } ∧ h == t } ∧ n == null

  28. Key observation  Method executions that logically modify the state have a simple LP.  Method executions that do not logically modify the state often have a complex LP. So: Treat these two cases differently.  Search for LPs of executions that logically modify the state;  Do a non-constructive proof for executions that do not logically modify the state.

Recommend


More recommend