Alternation for Termination William Harris, Akash Lal, Aditya Nori Sriram Rajamani
Termination bugs are a real problem in systems and application code.
“Gecko mediaplayer hangs the browser” “Eclipse hangs after 5 minutes or so of working” “BUG: Silverlight makes browser hang after BeginSaveChanges on some machines” “BUG: VB Hangs While Automating Excel Using OLE Control” … A Quick Search “bug code hangs”:
Key challenge to proving termination: Analyzing the context of a loop
An Example with Non-Trivial Context f(int d, z) { int x, y; while (x > 0 && y > 0) { if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); }
Local Termination Provers For a fixed over-approximation of a loop, find a proof of termination
Local Provers Succeeding while (x > 0 && y > 0) { assume(d > 0); if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } y x
Local Provers Failing f(int d) { int x, y; while (x > 0 && y > 0) { assume(d > 0); if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { f(1); f(2); } ??
Transition Invariants From stem and cycle of a loop, guess and check a proof of termination
Advantage of Transition Invariants A stem to a loop can include information about the loop’s context.
Transition Invariants Succeeding f(int d) { while (x > 0 && y > 0) { if (*) { x := x – d; y := *; } else { y := y – d; } } main() { f(1); f(2); } while (x > 0 && y > 0) { x := x – d; y := *; } x
Transition Invariants Succeeding f(int d) { while (x > 0 && y > 0) { if (*) { x := x – d; y := *; } else { y := y – d; } } main() { f(1); f(2); } while (x > 0 && y > 0) { y := y - d; } y
Disadvantage of Transition Invariants Stem and cycle can lead to incorrect guesses for proof of termination.
Transition Invariants Failing f(int d) { f(int d, int z) { int x, y; while (x > 0 && y > 0) { if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1); f(1, z); f(2); f(2, z); }
Key Insight of TREX From cycles through a loop, infer invariants for proving termination.
Context Analysis via TREX f(int d, z) { int x, y; while (x > 0 && y > 0) { assume(d > 0); if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); }
Payoff of TREX’s Approach TREX can apply local provers to find a proof of termination quickly
Analysis via TREX f(int d, z) { int x, y; while (x > 0 && y > 0) { assume(d > 0); if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); } x, y
TREX in More Detail TREX by example Experiments
TREX iteratively finds a proof of termination, or finds a counterexample to termination, or refines stronger program invariants The TREX Algorithm
TREX Iteration Step 1 Find a proof of termination by applying a local termination prover
f(int d, z) { int x, y; while (x > 0 && y > 0) { if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); } TREX Iteration Step 1 ??
TREX Iteration Step 2 If local prover fails, then find a counterexample cycle
TREX Iteration Step 2 f(int d, z) { int x, y; while (x > 0 && y > 0) { if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); } while (x > 0 && y > 0) { y := y – d; }
TREX Iteration Step 3 From the counterexample cycle, find a sufficient condition for non-termination by applying a non-termination prover (TNT)
Applying a Non-Termination Prover while (x > 0 && y > 0) { y := y – d; } Non-termination if: y > 0 && d <= 0
TREX Iteration Step 4 Check if the sufficient condition is reachable
TREX Iteration Step 4 f(int d, z) { int x, y; while (x > 0 && y > 0) { assert(d > 0); if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); } while (x > 0 && y > 0) { y := y – d; } Non-termination if: y > 0 && d <= 0
TREX Iteration Step 5 If the sufficient condition is unreachable, then assume this as an invariant.
TREX Iteration Step 5 f(int d, z) { int x, y; while (x > 0 && y > 0) { assert(d > 0); if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1, z); f(2, z); } assume(d > 0); x, y
Experiments Windows Vista driver snippets
Vista Driver Snippets Driver Name TREX time (s)Terminator* time (s)TREX speedup TO
Conclusion TREX proves termination by using cycles through a loop to infer useful program invariants
Extra slides
Transition Invariants Succeeding f(int d) { while (x > 0 && y > 0) { if (*) { x := x – d; y := *; } else { y := y – d; } main() { f(1); f(2); } x, y
Transition Invariants Failing f(int d) { f(int d, int z) { int x, y; while (x > 0 && y > 0) { if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1); f(1, z); f(2); f(2, z); } while (x > 0 && y > 0) { assume(d = 1 && z = 1); if (*) { x := x – d; y := *; z := z – 1; } z - 1 z = 1; f(1, z);
Transition Invariants Failing f(int d) { f(int d, int z) { int x, y; while (x > 0 && y > 0) { if (*) { x := x – d; y := *; z := z – 1; } else { y := y – d; } } } main() { int k; int z = 1; while (z < k) { z := 2 * z; } f(1); f(1, z); f(2); f(2, z); } while (x > 0 && y > 0) { assume(d = 1 && z = 2); if (*) { x := x – d; y := *; z := z – 1; } z - 2 z = 1; z := 2 * z; f(1, z);