ZING Systematic State Space
Exploration of Concurrent Software
Jakob RehofMicrosoft Research
http://research.microsoft.com/~rehofhttp://research.microsoft.com/zing
Joint work withTony Andrews (MS)Shaz Qadeer (MSR)
Sriram K. Rajamani (MSR)
Lecture I : Outline
• Software Model Checking
• Overview of ZING
Software Validation
• Large scale reliable software is hard to build and test.
• Different groups of programmers write different components.
• Integration testing is a nightmare.
Property Checking
• Programmer provides redundant partial specifications
• Code is automatically checked for consistency
• Different from proving whole program correctness – Specifications are not complete
Interface Usage Rules
•Rules in documentation–Incomplete, unenforced, wordy
–Order of operations & data access
–Resource management
•Disobeying rules causes bad behavior
–System crash or deadlock
–Unexpected exceptions
–Failed runtime checks
Does a given usage rule hold?
• Checking this is computationally impossible!
• Equivalent to solving Turing’s halting problem (undecidable)
• Even restricted computable versions of the problem (finite state programs) are prohibitively expensive
Why bother?
Just because a problem is undecidable, it doesn’t go away!
Automatic property checking = Study of tradeoffs
• Soundness vs completeness – Missing errors vs reporting false alarms
• Annotation burden on the programmer
• Complexity of the analysis– Local vs Global– Precision vs Efficiency– Space vs Time
Broad classification
• Underapproximations– Testing
• After passing testing, a program may still violate a given property
• Overapproximations– Type checking
• Even if a program satisfies a property, the type checker for the property could still reject it
Current trend
• Confluence of techniques from different fields:– Model checking– Automatic theorem proving– Program analysis
• Significant emphasis on practicality
• Several new projects in academia and industry
Model Checking• Algorithmic exploration of state space of the
system
• Several advances in the past decade: – symbolic model checking– symmetry reductions– partial order reductions– compositional model checking– bounded model checking using SAT solvers
• Most hardware companies use a model checker in the validation cycle
enum {N, T, C} state[1..2]
int turn
init
state[1] = N; state[2] = N
turn = 0
trans
state[i]= N & turn = 0 -> state[i] = T; turn = i
state[i] = N & turn !=0 -> state[i] = T
state[i] = T & turn = i -> state[i] = C
state[i] = C & state[2-i+1] = N -> state[i] = N
state[i] = C & state[2-i+1] != N -> state[i] = N; turn = 2-i+1
N1,N2turn=0
T1,N2turn=1
T1,T2turn=1
C1,N2turn=1
C1,T2turn=1
N1,T2turn=2
T1,T2turn=2
N1,C2turn=2
T1,C2turn=2
N = noncritical, T = trying, C = critical
Model Checking• Strengths
– Fully automatic (when it works)– Computes inductive invariants
• I such that F(I) I
– Provides error traces
• Weaknesses– Scale– Operates only on models
• How do you get from the program to the model?
Theorem proving– Early theorem provers were proof checkers
• They were built to support assertional reasoning in the Hoare style
• Cumbersome and hard to use
– Greg Nelson’s thesis in early 80s paved the way for automatic theorem provers• Theory of equality with uninterpreted functions• Theory of lists• Theory of linear arithmetic• Combination of the above !
– Automatic theorem provers based on Nelson-Oppen method are widely used• ESC• Proof Carrying Code
Theory of Equality. • Symbols: =, , f, g, …• Axiomatically defined:
E = E
E2 = E1
E1 = E2
E1 = E2 E2 = E3
E1 = E3
E1 = E2
f(E1) = f(E2)
• Example of a satisfiability problem: g(g(g(x)) = x g(g(g(g(g(x))))) = x g(x) x
• Satisfiability problem decidable in O(n log n)
a : array [1..len] of int;
int max := -MAXINT;i := 1;{ 1 j i. a[j] max}while (i len)
if( a[i] > max) max := a[i];
i := i+1;endwhile{ 1 j len. a[j] max}
( 1 j i. a[j] max) ( i > len)
( 1 j len. a[j]
max}
Automatic theorem proving
• Strengths– Handles unbounded domains naturally– Good implementations for
• equality with uninterpreted functions• linear inequalities• combination of theories
• Weaknesses– Hard to compute fixpoints– Requires inductive invariants
• Pre and post conditions• Loop invariants
Program analysis
• Originated in optimizing compilers– constant propagation– live variable analysis– dead code elimination– loop index optimization
• Type systems use similar analysis• Are the type annotations consistent?
Program analysis• Strengths
– Works on code – Pointer aware– Integrated into compilers– Precision efficiency tradeoffs well studied
• flow (in)sensitive• context (in)sensitive
• Weakenesses– Abstraction is hardwired and done by the
designer of the analysis– Not targeted at property checking
(traditionally)
Model Checking, Theorem Proving and Program Analysis
• Very related to each other
• Different histories– different emphasis– different tradeoffs
• Complementary, in some ways
• Combination can be extremely powerful
What is the key design challenge in a model checker for software?
It is the model!
Model Checking Hardware
Primitive values are booleans
States are boolean vectors of fixed size
Models are finite state machines !!
Characteristics of Software
Primitive values are more complicated– Pointers– Objects
Control flow (transition relation) is more complicated– Functions– Function pointers– Exceptions
States are more complicated – Unbounded graphs over values
Variables are scoped– Locals– Shared scopes
Much richer modularity constructs– Functions– Classes
When I use a model checker, it runs and runs for ever and never comes back… when I use a static analysis tool, it comes back immediately and says “I don’t know”
- Patrick Cousot
Computing power doubles every 18 months
-Gordon Moore
Problem
• Check if programs written in common programming languages (C, C++, C#, Java) satisfy certain safety properties
• Examples of properties:– API usage rules – ordering of calls– Absence of races– Absence of deadlocks– Protocol (state machines) on objects– Language-based safety properties
Approach• Extract abstract “model” from the program that
captures all “relevant” portions of the program with respect to property of interest
• Systematically explore the state space of the extracted model.
• Example: SLAM– Check if a sequential C program uses an interface
“correctly” as specified by a safety property, using boolean program models
Sequential C program
Finite state machines
Source code
FSM
modelchecker
Traditional approach
Sequential program in rich programming language (eg. C)
Finite state machines
Source code
FSM
abstraction
modelchecker
C data structures, pointers,procedure calls, parameter passing,scoping,control flow
Software model checking
Boolean program
Data flow analysis implemented using BDDs
SLAM
Push down model
Related work: BLAST, MAGIC,…
Source code
abstraction
modelchecker
Zing
Rich control constructs: thread creation, function call, exception, objects, dynamic allocation
Model checking is undecidable!
Device driver (taking concurrency into account), web services code, transaction management system (2pc)
Source code
abstraction
modelchecker
Zing
3 core constructs:
1. Procedure calls with call-stack
2. Objects with dynamic allocation
3. Threads with dynamic creation
Inter-process communication:
1. Shared memory
2. Channels with blocking-receives, non-blocking sends, FIFO
Concurrent program in rich programming language
Lecture I : Outline
• Software Model Checking
• Overview of ZING
Zing: Challenges and Approach
• Handling programming language features– Compile Zing to an intermediate “object
model” (ZOM)– Build model checker on top of ZOM
• State explosion– Expose program structure in ZOM– Exploit program structure to do efficient model
checking
State
Heap: complex types
…
ProcessProcess
Process
…Processes
Zing Object Model: Internal StateView
Globals: simple types & refs
Stack
IPLocalsParams
IPLocalsParams
…
Zing Object Model: External State View
• Simplified view to query and update state– How many processes?– Is process(i) runnable?– Are two states equal?– Execute process(i) for one atomic step
• Can write simple DFS search in 10 lines
private void doDfs(){ while(stateStack.Count > 0){
State s = (State) stateStack.Peek(); bool foundSuccessor = false;
// find the next process to execute and execute it for (int p = s.LastProcessExplored + 1; p < s.NumProcesses; p++) {
if(s.RunnableProcesses[p] {
State newS = s.Execute(p); if (!stateHash.contains(newS)){ stateHash.add(newS);
stateStack.push(newS); foundSuccessor = true;
break; }
} } if(!foundSuccessor) stateStack.Pop(); }}
DOESN’T SCALE
NEED TO EXPLOIT PROGRAM STRUCTURE !
Optimizations to make model checking scale
• Exploring states efficiently – Finger-printing– State-delta– Parallel model checking with multiple nodes
• Exploring fewer states (using mathematical properties)– Reduction– Summarization– Symbolic execution– Iterative Refinement– Compositional conformance checking
Saving storage
• Only states on the checkers stack are stored
• For states not on the stack only a fingerprint is stored
• Store only “deltas” from previous states
State reduction• Abstract notion of
equality between states• Avoid exploring “similar”
states several times• Exploit structure and do
this fully automatically while computing the fingerprint:
s1 s2 f(s1) = f(s2)
Heap1
a
100
b 0
200
Heap2
b 0
150
a
300
ptr
ptr
Architecture & Communication
ModelChecker
ModelChecker
ModelChecker
ModelChecker
Reachablestates Frontier
Trac
e
Reach
ed S
tate
s
Fron
tier S
tate
s
Server
Optimizations to make model checking scale
• Exploring states efficiently – Finger-printing– State-delta– Parallel model checking with multiple nodes
• Exploring fewer states (using mathematical properties)– Reduction– Summarization– Symbolic execution– Iterative Refinement– Compositional conformance checking
Racy program: need to explore all interleavings!
local int y = 0; x := x + 1; x := x + 1; x := x + 1; x := x +1;
assert(x div 4);
y = y+1;y = y+1;
//initialize int x :=0;
local int z = 0; x := x + 1; x := x + 1; x := x + 1; x := x +1;
assert(x div 4);
z = z+1;z = z+1;
Race-free program: need to explore two interleavings!
local int y;acquire (m); x := x + 1; x := x + 1; x := x + 1; x := x +1;
assert(x div 4);release (m);
y = y+1;y = y+1;
//initialize int x :=0;mutex m;
local int z;acquire (m); x := x + 1; x := x + 1; x := x + 1; x := x +1;
assert(x div 4);release (m);
z = z+1;z = z+1;
Four atomicitiesS0 S1 S2
acq(this) x
S0 T1 S2
x acq(this)
S7T6S5
rel(this) z
S7S6S5
rel(this)z
S2 S3 S4
r=bal y
S2 T3 S4
r=baly
S2 T3 S4
r=bal x
S2 S3 S4
r=balx
• R: right movers– lock acquire
• L: left movers– lock release
• B: both right + left movers– variable access holding
lock• N: non-movers
– access unprotected variable
Transaction
S0. S5
R* N L*x Y. . .
S0. S5
R* N L*x Y. . .
Other threads need not be scheduled in the middle of a transaction
Lipton ‘75: any sequence (R+B)*; (N+) ; (L+B)* is a transaction
Recall example:each thread has one transaction!
local int y;acquire (m); x := x + 1; x := x + 1; x := x + 1; x := x +1;
assert(x div 4);release (m);
y = y+1;y = y+1;
//initialize int x :=0;mutex m;
local int z;acquire (m); x := x + 1; x := x + 1; x := x + 1; x := x +1;
assert(x div 4);release (m);
z = z+1;z = z+1;
Transaction-based reduction
• ZOM extended to expose “mover-ness” of each action
• Model checker maintains a state machine to track the “phase” of each transaction
• Continues scheduling one thread as long as it is inside a transaction!
• Current implementation:– Classifies all heap accesses as non-movers
– Can improve the scalability using better analysis (ownership?)
Optimizations to make model checking scale
• Exploring states efficiently – Finger-printing– State-delta– Parallel model checking with multiple nodes
• Exploring fewer states (using mathematical properties)– Reduction– Summarization– Symbolic execution– Iterative Refinement– Compositional conformance checking
Summarization for sequential programs
• Procedure summarization (Sharir-Pnueli 81, Reps-Horwitz-Sagiv 95) is the key to efficiency
int x;
void incr_by_2() { x++; x++;}
void main() { … x = 0; incr_by_2(); … x = 0; incr_by_2(); …}
• Bebop, ESP, Moped, MC, Prefix, …
What is a summary in sequential programs?
• Summary of a procedure P =
Set of all (pre-state post-state) pairs obtained by invocations of P
int x;
void incr_by_2() { x++; x++;}
void main() { … x = 0; incr_by_2(); … x = 0; incr_by_2(); … x = 1; incr_by_2(); …}
x x’
0 21 3
Assertion checking for sequential programs
• Boolean program with:– g = number of global vars– m = max. number of local vars in any scope– k = size of the CFG of the program
• Complexity is O( k 2 O(g+m)
), linear in the size of CFG
• Summarization enables termination in the presence of recursion
Assertion checking for concurrent programs
There is no algorithm for assertion checking of concurrent boolean programs, even with only two threads [Ramalingam 00]
Our approach
• Precise semi-algorithm for verifying properties of concurrent programs– based on model checking– procedure summarization for efficiency
• Termination for a large class of concurrent programs with recursion and shared variables
• Generalization of precise interprocedural dataflow analysis for sequential programs
What is a summary in concurrent programs?
• Unarticulated so far• Naïve extension of summaries for sequential
programs do not work• [Qadeer, Rajamani, Rehof. POPL 2004]
Optimizations to make model checking scale
• Exploring states efficiently – Finger-printing– State-delta– Parallel model checking with multiple nodes
• Exploring fewer states (using mathematical properties)– Reduction– Summarization– Symbolic execution– Iterative Refinement– Compositional conformance checking
Symbolic execution• Go from “closed” to “open” programs• Can think about this as “delayed case-split”• Integrating Zing’s state exploration engine with Zap
theorem prover from TVM group• States are now extended to have a concrete part and
symbolic part– Concrete part is fingerprinted– Symbolic part is manipulated by theorem prover
• How do we handle symbolic references (pointers)?
Iterative refinement, ala SLAM
#include <ntddk.h>
C2BPpredicate abstraction
booleanprogram
Newtonfeasibility
check
Bebopreachability
check
HarnessSLICRule
+
refinementpredicates
errorpath
Iterative refinement in Zing
Pointerabstraction
AbstractZing
program
PointerRefinement
ZingMC
Zingprogram +
hint
errorpath
Optimizations to make model checking scale
• Exploring states efficiently – Finger-printing– State-delta– Parallel model checking with multiple nodes
• Exploring fewer states (using mathematical properties)– Reduction– Summarization– Symbolic execution– Iterative Refinement– Compositional conformance checking
Conformance
• Conformance theory– P < Q? Does one model (P, implementation) conform
to another (Q, specification)?
• Compositional– P < Q => C[P] < C[Q]
• Theory developed for CCS – [Fournet, Rajamani, Hoare, Rehof. CAV 2004]
• Implemented modularly on top of ZING (ZOM)• Used in integrated checker for message-passing
programs
Context-bounded model checking
• Motivation: Many concurrency bugs manifest within relatively few context switches (5-10)
• If we bound the number of context switches, we can explore (unbounded) concurrent stack machines completely, up to the bound
• [Qadeer, Rehof. TACAS 2005]