parameterized verification of thread-safe libraries thomas ball sagar chaki sriram k. rajamani
TRANSCRIPT
Motivation Object-oriented (O-O) design very common
Libraries export a set of classes Internal state maintained via member variables
Multithreading becoming more prevalent O-O libraries designed to be thread-safe
Usually via some locking mechanism How can you check if your library is thread
safe?
Stack of integersclass IntStack {
private:public:
};
int *Data; int Size;
void Push(int newTop) {Data[Size++] = newTop;
}
int Pop(void) {if(Size > 0) return Data[--Size];else return –1;
}
Making it thread-safeclass IntStack {
private: int *Data; int Size;
public:void Push(int newTop) {
Data[Size++] = newTop;
}};
mutex Lock;
Lock.lock();
Lock.unlock();
More precisely … How do you know the value of Size is
always non-negative ? Ok … let us make it simpler … Assuming at most two threads can
access the stack simultaneously, how do you answer the above question ?
That’s easy …
main()Push()Pop()
Size
main()Push()Pop()
• main calls Push and Pop non-deterministically
• Push and Pop manipulate Size
• Allow lock and unlock operations
• Verify AG(Size >= 0)
Thread 1 Thread 2Shared
Can we model check this? Will not work in general
Specifically if you allow recursion Not even with just two threads Not even with just boolean variables The problem is undecidable
P’
B
Is ERROR reachable?
Yes, path p No
Return “No”Is p feasible in P?
Is ERROR reachable?
don’t know
No, explanation Yes
Return “Yes”, pReturn “don’t know”
Bebop
Newton
C2bp
Predicates from instrumentation
Model checking multi-threaded boolean programs
B1 | B2
Given two CFGs G1 and G2, whether they have a non-empty intersection is undecidable
You can reduce this question to a safety (reachability) question for two boolean programs which communicate via shared variables and interleave arbitrarily
FSM Abstractions Number of threads not known in
advance Possible solutions
Model check a finite number of threads
Use parameterized model checking and handle an arbitrary number of threads
So now what ? Number of threads not known in
advance Possible solutions
Model check a finite number of threads Blows up pretty quickly
Use parameterized model checking and handle an arbitrary number of threads
Can handle some examples even when instantiation to finite number of threads blows up!!
Parameterized Library System Given a LGFSM F and a positive integer
n, F(n) denotes a library system composed of n copies of F n copies of L initialized to IL 1 copy of G initialized to IG Global configuration : [g,l1, … , ln] Each copy of F executes asynchronously but
only one copy executes at a time
PLS Reachability Global state g is reachable in F(n) if
and only if some configuration [g,l1, … ln] is reachable
Given a LGFSM F and a global state g, is there a positive integer n such that g is reachable in F(n) ? Parameterized reachability problem
This problem is decidable
L1G1
L1
L2G2
L1
L1G2
L2
L1G3
L1
(Global state, #of processes in L1, #of processes in L2)
(G1,2,0)
(G2,1,1)
(G2,1,1)
(G3,2,0)
Configurations: an alternate representation
Relations on configurations (g,l1, … , ln) = (g’,l1’, … , ln’) iff
g = g’ and li = li’ for each I
(g,l1, … , ln) (g’,l1’, … , ln’) iff g = g’ and li li’ for each i
C < C’ iff C C’ and C != C’
Reachability on configurations Start with initial configuration
Keep exploring new configurations from current configurations.
Follow two rules!
Rule 1
If C1 is a new configuration and there exists a reachable configuration C2 such that C1 C2
drop C1 !
Rule 2
If C1 is a new configuration and there exists an ancestor C2 such that C2 C1
Replace ancestor with “*”s appropriately !
Claim The procedure is sound and complete
Every reachable state is discovered Every discovered state is reachable
The procedure always terminates Dickson’s lemma
Complexity The reachability tree traversal can take non-
primitive recursive space in # of local states Space lower bound O(2^sqrt(n))
Lipton 1976 Algorithm with space O(2^n.log(n)) exists
Rackoff 1978 Impractical – most people use some form of
reachability tree traversal Closely related with Petri nets and VAS
Coverability problem for Petri nets
Beacon An implementation of the above
algorithm Inputs : a LGFSM and a global state
transitions expressed by guarded commands
Output : answer to the parameterized reachability problem
Implementation tricks Construct reachability tree (instead of
graph) Need to check subsumption only on parents
along one path Represent * as largest unsigned integer
Check for overflows Data structure tricks to check
subsumption quickly Represent state sets as trees as well
Rockall Thread-safe O-O memory manager
developed at MSR by Michael Parkes
Memory organized in buckets Each bucket manages memory blocks of
a given size Buckets arranged in a tree like hierarchy.
Each bucket gets memory from a bigger bucket
Topmost bucket gets memory from the OS Several locks are used
Rockall LGFSM was constructed manually
Worst case # of steps O(10 ^ 10 ^ 600)
Checked that no memory location was allocated or freed more than once in a row 4 hours on a 800 MHz Pentium III with 512 MB Explored 2 million configurations Finite version with 4 threads did not terminate
with SMV !!
What about Java? Parameterized verification of Java
libraries Can model:
Synchronized Wait Notify
Cannot model Notify-all Can be handled using backward reachability
starting with updward closed sets