1
Eran Yahav Mooly SagivSchool of Computer Science
Tel-Aviv University
{yahave,msagiv}@post.tau.ac.ilhttp://www.cs.tau.ac.il/~yahave
Automatically Verifying Concurrent Queue Algorithms
2
Automatically Verifying Partial Correctness of Softwareusing Abstract Interpretation
Operates on the program source Fully automatic Conservative results
No errors are reported partial correctness is guaranteed
But may produce “false alarms”Makes the results non-useful
The Challenge avoid false alarms
3
Concurrent Queues
A common component of concurrent systems Operating systems
A large number of suggested algorithmsHard to get right
[Stone90] – races + items may be lost [Valois94] – items may be lost …
Mostly given without formal proof of correctness
4
Automatically Verifying Concurrent Queue Algorithms?
Support the following Concurrency Dynamic allocation/deallocation of objects Destructive updates Heap references Unbounded storage (heap) Dynamic allocation/deallocation of threads
Handling references with sufficient precision for establishing correctness properties Preceding pointer-analysis phase usually
insufficient
5
Example [Michael&Scott PODC96]
public void enqueue(Object value) { e_1 node = new QueueItem() // allocate queue nodee_2 node.val = value // copy enqueued value into nodee_3 node.next.ref = NULL e_4 while(true) { // Keep trying until done e_5 tail = this.Tail // get Tail.ptr and Tail.count e_6 next = tail.ref.next // get next ptr and count e_7 if (tail == this.Tail) { // are tails consistent?e_8 if (next.ref == NULL) { // was tail pointing to last node?e_9 if CAS(tail.ref.next,
next, <node, next.count+1>) { // try connecte_10 break // Enqueue is done. Exit loop e_11 }e_12 } else { // tail wasn’t pointing to last
nodee_13 CAS(this.Tail, tail,<next.ref, tail.count+1>) // try advance taile_14 } e_15 } e_16 } e_17 CAS(this.Tail, tail, <node, tail.count+1>) //enqueue done. try swing tail e_18 }
6
Correctness
P1 The linked list is always connectedP2 Nodes are only inserted after the last
node of the linked listP3 Nodes are only deleted from the
beginning of the linked listP4 Head always points to the first node in
the linked listP5 Tail always points to a node in the linked
list
7
Rich Problem Expressive Formalism
We use first-order logic with transitive closure
Naturally define behavior of heap-manipulating programs Heap references Heap Reachability Threads as heap-allocated objects (and
scheduling)
Can also model integers
8
Plan
Vanilla verification attempt Program configurations Expressing safety properties Abstraction
Refining the vanilla solution Instrumentation predicates
Prototype implementation (TVLA/3VMC)
9
Configurations
A program configuration encodes global store program-location of every thread status of locks and threads
First-order logical structures used to represent program configurations
10
Configurations as First-order Logical Structures
First-order structure Objects - Individuals properties of objects – unary predicates relationship between objects – binary predicates Additional integrity constraints FO formulas
Object Type – unary predicates References between objects – binary predicates
Thread Program location – unary predicates
Integers Distinguished zero individual – unary predicate Successor relationship – binary predicate With integrity constraints corresponding to Peano axioms
11
at[e_2]
at[e_2]
zerosuccsucc succ
rv[node]
rv[node]
rv[this]
rv[this]
rv[Head] rv[next] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]
iv[next]iv[next]
Concrete Configuration
rv[value]
rv[value]
12
Configurations
Predicates model properties of interest eq(v1,v2) is_T(v) { at[lab](t) : lab Labels } { rv[fld](o1,o2) : fld Fields }
{ iv[fld](o1,o2) : fld Fields } heldBy(l,t), blocked(t,l), waiting(t,l) zero(v), succ(v1,v2)
Can use the framework with different predicates
13
Safety PropertiesProperty Property Formula
P1 Tail reachable from Head
q:nbq,vt. rv[Tail](q,vt)
vh. rv[Head](q,vh) rv[next]*(vh, vt)
P2 Insert after last
q:nbq,ti:thread,vi,vt. at[e_18](ti) rv[node](ti,vi) rv[tail](ti,vt)
rv[this](ti,q) rv[next](vt,vi) rv[Tail](q,vi)
P3 Delete first
q:nbq,td:thread,vd,vh. at[d_19](td) rv[head](td,vd)
rv[this](td,q) rv[Head](q,vh) rv[next](vd,vh)
P4 Head first q:nbq,v,u. rv[Head](q,v) rv[next](u,v)
P5 Tail exists
q:nbq. v. rv[Tail](q,v)
14
at[e_2]
at[e_2]
zerosuccsucc succ
rv[node]
rv[node]
rv[this]
rv[this]
rv[Head] rv[next] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]
iv[next]iv[next]
Tail Reachable from Head
Vh Vt
q:nbq,vt. rv[Tail](q,vt)
vh. rv[Head](q,vh) rv[next]*(vh, vt)
rv[value]
rv[value]
15
Abstract Program Model
Conservative representation of the concrete model
Use 3-valued logical structures to conservatively represent multiple 2-valued structures 1 = true 0 = false 1/2 = unknown A join semi-lattice, 0 1 = 1/2
Conservatively apply actions on abstract configurations
16
at[e_2]
at[e_2]
zerosuccsucc succ
rv[node]
rv[node]
rv[this]
rv[this]
rv[Head] rv[next] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]
iv[next]iv[next]
Concrete Configurationrv[value]
rv[value]
17
zerosucc
rv[Head]
iv[Tail]
iv[Head]
Abstract Configuration
at[e_2]
rv[this]
iv[next]
rv[node]
rv[this]
rv[next]
succ
rv[Tail]
rv[value]
18
canonical Abstraction
Merge all nodes with the same unary predicate values into a single summary node
Join predicate valuesConverts a configuration of infinite size
into a 3-valued abstract configuration of bounded size
19
at[e_2]
at[e_2]
zerosuccsucc succ
rv[node]
rv[node]
rv[this]
rv[this]
rv[Head] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]
iv[next]
Concrete Bad Configuration
q:nbq,vt. rv[Tail](q,vt) vh. rv[Head](q,vh) rv[next]*(vh, vt)
rv[value]
rv[value]
20
at[e_2]
zerosucc
rv[Head]
iv[Tail]
iv[Head]
Abstract Configuration
at[e_2]
rv[this]
iv[next]
rv[this]
rv[next]
succ
rv[Tail]
q:nbq,vt. rv[Tail](q,vt) vh. rv[Head](q,vh) rv[next]*(vh, vt)
rv[node]
rv[value]
21
Instrumentation
Refine the abstraction by recording additional information
Natural idea – record which property-formulae hold via nullary predicates Corresponds to predicate abstraction
More generally – record subformulae that hold for an individual via unary predicates Obtain (some) useful results without changing
set of predicates per program/property
22
Instrumentation Predicates
Predicate Intended Meaning Defining Formula
is[fld](o) o is shared by fld of at least two different objects
v1,v2.eq(v1,v2) rv[fld](v1,o) rv[fld](v2,o)
exists[fld](o)
There exists an object referenced by fld of o
v.rv[fld](o,v)
is_acquired(l)
l is acquired by some thread t.heldBy(l,t)
rt[fld,n](o) o is reachable from object referenced by fld using a path of next fields
t,ot.rv[fld](t,ot) rv[next]*(ot,o)
r_by[fld](o) o is referenced by fld of some object
v.rv[fld](v,o)
i_by[fld](o) o is int value of fld of some object
v.iv[fld](v,o)
23
r_by[node]rt[node,n]
is[this]r_by[this]rt[this,n]
r_by[node]rt[node,n]
r_by[head]rt[head,n]
r_by[next]rt[Head,n]
r_by[next]rt[Head,n]
r_by[next]r_by[Tail]rt[Head,n]rt[Tail,n]
at[e_2]
at[e_2]
zeroi_by[…]
succsucc succ
rv[node]
rv[node]
rv[this]
rv[this]
rv[Head] rv[next] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]
iv[next]iv[next]
Instrumented Concrete Configuration
r_by[value]rt[value,n]
r_by[value]rt[value,n]
rv[value]
rv[value]
24
r_by[node]rt[node,n]
is[this]r_by[this]rt[this,n]
r_by[Head]rt[Head,n]
r_by[next]rt[Head,n]
r_by[next]r_by[Tail]rt[Head,n]rt[Tail,n]
at[e_2]
rv[node]
rv[this]
rv[Head] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]iv[next]rv[next]
succ
succ
Instrumented Abstract Configuration
zeroi_by[…]
q:nbq,vt. rv[Tail](q,vt)
vh. rv[Head](q,vh) rv[next]*(vh, vt)q:nbq,v. rv[Tail](q,v) rt[Head,n](v)
r_by[value]rt[value,n]rv[value]
26
Best Conservative Interpretation
Concrete Representati
on
Concrete Representati
on
Concretization Abstraction
Operational Semantics
[|S|]
Abstract Representati
on
Abstract Representati
onAbstract Semantics
[|S|]
27
r_by[node]rt[node,n]
is[this]r_by[this]rt[this,n]
r_by[Head]rt[Head,n]
r_by[next]rt[Head,n]
r_by[next]r_by[Tail]rt[Head,n]rt[Tail,n]
at[e_2]
rv[node]
rv[this]
rv[Head] rv[next] rv[next]
rv[Tail]
iv[Tail]
iv[Head]
iv[next]iv[next]rv[next]
succ
succ
zeroi_by[…]
r_by[value]rt[value,n]rv[value]
Abstract Interpretation - Concretizatione_2 node.val = value
28
r_by[node]rt[node,n]at[e_2]
rv[node]
Abstract Interpretation - Concretizationr_by[value]rt[value,n]rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
…
e_2 node.val = value
29
Abstract Interpretation - update
r_by[node]rt[node,n]exists[val]
at[e_3]rv[node]
r_by[value]rt[value,n]r_by[val]rt[val,n]
rv[value]
r_by[node]rt[node,n]exists[val]
at[e_3]rv[node]
r_by[value]rt[value,n]r_by[val]rt[val,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]exists[val]
at[e_3]rv[node]
r_by[value]rt[value,n]r_by[val]rt[val,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
r_by[node]rt[node,n]at[e_2]
rv[node]
r_by[value]rt[value,n]
rv[value]
…rv[val]
rv[value] rv[value]
e_2 node.val = value
30
r_by[node]rt[node,n]
at[e_2]
rv[node]
Abstract Interpretation - abstraction
r_by[value]rt[value,n]rv[value]
at[e_3]
rv[value]
rv[val]
rv[node]
r_by[value]rt[value,n]r_by[val]rt[val,n]
r_by[node]rt[node,n]exists[val]
at[e_3]
rv[value]
rv[val]
rv[node]
r_by[value]rt[value,n]r_by[val]rt[val,n]
r_by[node]rt[node,n]exists[val] r_by[node]
rt[node,n]
at[e_2]
rv[node]
r_by[value]rt[value,n]rv[value]
at[e_3]
rv[value]
rv[val]
rv[node]
r_by[value]rt[value,n]r_by[val]rt[val,n]
r_by[node]rt[node,n]exists[val]
31
Prototype Implementation
TVLA/3VMCfocuscoerceLimitations
only intraprocedural no optimizations used
No partial-order reduction
32
Experimental Results
Program Configs Space (MB)
Time (Sec)
Nbq_enqueue 1833 14.2 727
Nbq_dequeue 1098 5.3 309
Nbq_err1 36 0.1 11
Nbq_uni 17 0.1 3
Tlq_enqueue 982 10 6162
Tlq_dequeue 225 4.1 304
Twolockqn 975 7.5 577
Twolockq_err1 24 0.1 30
33
Conclusions
Common challenges of model checking and abstract interpretation False alarms Cost Scalability
Size Language features
34
Conclusions
Traditional MC Our Approachconcrete configuration
propositional FOTC
abstraction model extraction*abstract interpretation
control flowinternally represented
internally represented
materialization ? basic featureobject number a priori bounded unbounded
thread numberfixed or a priori bounded
unbounded
abstraction refinement
yes ?
35
Summary
Verified interesting safety properties of concurrent queues
Unbounded number of objects and threads
Dynamic allocation of objects and threads
36
http://www.cs.tau.ac.il/~yahave
37
Integer Representation - Concrete
zerosuccsucc succ
iv[y]iv[x]
y++
x == y ?
yes
zerosuccsucc succ
iv[y]
iv[x]x == y ?
no
38
Integer Representation – No Instrumentation
zerosuccsucc succ
iv[y]iv[x]
y++
x == y ?
yes
zerosuccsucc succ
iv[y]iv[x]
x == y ?
no
39
Integer Representation – No Instrumentation
zerosucc
iv[y]iv[x]
y++
x == y ?
maybe
x == y ?
maybe
succ
zerosucc
iv[y]iv[x]
succ
40
Integer Representation – With Instrumentation
zerosuccsucc succ
iv[y]iv[x]
y++
x == y ?
yes
zerosuccsucc succ
iv[y]iv[x]
x == y ?
no
i_by[x]i_by[y]
i_by[x] i_by[y]
41
Integer Representation – With Instrumentation
zerosucc
iv[y]
iv[x]
y++
x == y ?
yes
x == y ?
no
succ
succ
zerosucc
iv[y]
iv[x]
succ
succ
succi_by[x] i_by[y]
i_by[x]i_by[y]
42
Backup Slides
43
References
[Stone90] J.M. Stone. A simple and Correct Shared-Queue
Algorithm using compare-and-swap. In Proceedings of Supercomputing ’90, November 1990.
[Valois94] J.D. Valois. Implementing Lock-Free Queues. In
Seventh international Conference on Parallel and Distributed Computing Systems, Las Vegas, NV, October 1994
44
Structural Operational Semantics - actions
An action consists of: precondition formula update formulae
Precondition formula may use a free variable ts for “currently scheduled” thread
Semantics is non-deterministic
45
Structural Operational Semantics - actions
lock(v)
tts: rval[v](ts,l) held_by(l,t)
held_by’(l1,t1) = held_by(l1,t1) (l1 = l t1 = ts)
blocked’ (t1,l1) = blocked(t1,l1) ((l1 l) (t1 ts))
precondition
predicateupdate
46
Initialize(C0) {for each C C0
push(stack,C)}explore() { while stack is not empty { C = pop(stack) if not member(C,stateSpace) { verify(C) stateSpace = stateSpace {C} for each action ac for each C’ such that C ac C’ push(stack,C’) } }}
State Space Exploration
47
Unbounded Number of Threads
Exploit state-space symmetryPrevious work defined symmetry between
process names (indices)Thread location = thread propertycanonical names = symmetry between
properties