Threadmodular Abstraction Refinement Tom Henzinger Ranjit Jhala Rupak
Thread-modular Abstraction Refinement Tom Henzinger Ranjit Jhala Rupak Majumdar [UC Berkeley] Shaz Qadeer [Microsoft Research]
Introduction • Model Checking Software – “Little theorems about big programs” – “automatic”, Path sensitive properties – Limited to sequential programs • Thread-modular Reasoning – Efficiently decompose checks – Requires manual (or divine) intervention • TAR: Thread-modular Abstraction Refinement – Eliminate the divine using abstraction-refinement – Safety checking for concurrent programs
The story so far. . . • Analyzing Sequential programs – BLAST/SLAM/… – Iterative Abstraction-Refinement [Kurshan ’ 93] Seed Abstraction Program Check Abstract explanation Why infeasible ? NO! (Trace) infeasible Refine Is model safe ? YES SAFE feasible BUG
… and what of Concurrent Programs? • Shared Memory (multithreaded) • Message Passing • Hard to analyze ! – Interleavings / State explosion • One approach: Thread-modular analysis – Analyse threads separately – Compose analyses • [Jones ’ 83, CALVIN (FQS ’ 02), Assume-Guarantee]
The Problem Safety checking: Is an ERROR state reachable ? • boxes = threads • white denotes shared variables
Thread-modular analysis safe (take 1)
Thread-modular analysis safe • If only… ! (take 1) safe Threads are correct in constrained environments
Second Attempt: Summaries • “Summarize” each thread’s behavior • Use/verify summaries (circular)
Use Summaries (“Assume”) safe
Verify Summaries (“Guarantee”) µ safe
Thread-modular analysis µ (take 2) µ safe
Our Contribution • Problem with TM Reasoning: – Divining (small) summaries ? • Algorithm TAR – Compute/use/verify summaries – Using iterative abstraction-refinement ?
An Example: Race Detection P´ Producer { Consumer { 1: while (*) { 2: while (flag) {}; 2: while (!flag) {}; 3: data = newdata(); 3: read = data; 4: flag = true; 4: flag = false; } } • Shared variables: data, flag, P, C • Error states: P Æ C • Initial states: : P Æ : C (Æ : flag) ´C
An Example: Race Detection P´ Producer { Consumer { 1: while (*) { 2: while (flag) {}; 2: while (!flag) {}; 3: data = newdata(); 3: read = data; 4: flag = true; 4: flag = false; } } • Correctness Invariant: – Producer ensures: P ) : flag – Consumer ensures: C ) flag ´C
Summaries Producer { 1: while (*) { 2: while (flag) {}; 3: data = newdata(); 4: flag = true; } } Consumer { 1: while (*) { 2: while (!flag) {}; 3: read = data; 4: flag = false; } } SProducer{ : flag !(flag’ Ç : flag’)Æ : P’ |: flag ! : flag’Æ P’ } SConsumer{ flag !(flag’ Ç: flag’) Æ : C’ | flag ! flag’ Æ C’ } • Summary: Set of (present state, next state) pairs
Checking Safety [CALVIN] Producer+{ Producer { 1: µ while (*) { while(*){s. Consumer (); } 2: safe while (flag) {}; while(*){s. Consumer (); } 3: data = newdata(); while(*){s. Consumer (); } 4: flag = true; } • [use] Sequential program: Producer+ use BLAST/SLAM/ESC/… • [verify] Every action of Producer+ is in SProducer • Where do summaries come from?
Abstraction & Reachability Error Initial • Abstraction gives finite state space • Conservative – Abstraction safe ) System safe – Too coarse ) spurious counterexample
Refinement • Using “spurious” error traces
Refinement • Using “spurious” error traces – Add information to rule out spurious trace – e. g. Track more variables or predicates • Repeat reachability – Till safe or real trace is found
Abstraction & Reachability safe • Using “spurious” error traces – Add information to rule out spurious trace – e. g. Track more variables or predicates • Repeat reachability – Till safe or real trace is found
To Summarize Reachability Tree ’ • Nodes labeled by abstract states • Each parent-child pair ! (present, next) pair – Quantify out local state (e. g. program counter) – Take pairs where global state changes
Tying up the threads Not yet the reachable set! Producer+{ 1: while (*) { while(*){s (); } Refine using 2: “spurious” error traces while (flag) {}; Consumer while(*){s. Consumer (); } 3: 4: data = newdata(); while(*){s. Consumer (); } } flag = true; ; ; Summarize
Refined System Fixpoint safe ; ;
Running TAR on Example P´ Producer { Consumer { 1: while (*) { 2: while (flag) {}; 2: while (!flag) {}; 3: data = newdata(); 3: read = data; 4: flag = true; 4: flag = false; } } • Shared variables: data, flag, P, C • Error states: P Æ C • Initial states: : P Æ : C Æ : flag ´C
Running TAR 1 P´ Reach: Producer { 1: while (*) { 2: while (flag) {}; 3: data = newdata(); 4: flag = true; } } Consumer { 1: while (*) { 2: while (!flag) {}; 3: read = data; ´ 4: flag = false; } } : PÆ: C Reach: : PÆ: C : PÆC PÆC ; ; Summary: : P Æ : C ! P’ Æ : C’ P Æ : C ! : P’Æ : C’ C Summary: Init: : P Æ : C Error: P Æ C Abs: P, C : P Æ : C ! : P’ Æ C’ : P Æ C ! : P’ Æ : C’
Running TAR 2 : flag P´ Reach: : P Æ : C Æ : flag : P Æ : C Æ flag : P Producer { 1: while (*) { 2: while (flag) {}; 3: data = newdata(); 4: flag = true; } } Consumer { 1: while (*) { 2: while (!flag) {}; 3: read = data; ´ 4: flag = false; } } ; Summary: : P Æ: flag ! P‘Æ : flag’ ! : P’ Æ flag’ P Æ : flag ! : P Æ: flag Only change if : flag ; Init: : P Æ : CÆ : flag Error: PFixpoint ÆC Abs: P, C, flag Track flag C Reach: : P Æ : C Æ : flag : P Æ C Æ flag : C Æ : flag Summary: C Æ flag ! : C’ Æ flag’ : C Æ flag ! : C’ Æ : flag’ ! C’Æ flag Only change if flag
Running TAR 2 P´ Reach: P Æ : C Æ : flag : P Producer { 1: while (*) { 2: while (flag) {}; 3: data = newdata(); 4: flag = true; } } Consumer { 1: while (*) { 2: while (!flag) {}; 3: read = data; ´ 4: flag = false; } } ; Summary: : flag ! (flag’ Ç : flag’) Æ : P’ ! : flag Æ P’ Reach: : P Æ C Æ flag : C safe ; C Summary: Fixpoint SUMMARIES LEARNT ! flag ! ( flag’ Ç : flag’) Æ : C’ ! flag’ Æ C’
Bells and Whistles • Havoc Abstractions: – Track only when the environment changes a variable • Not what new value it changes it to – For every global x, x denotes states where thread writes x • Summary: if ( x) then x = * • No explicit global variables – Sharing via pointers that escape
Race Detection w/ Pointers Producer { } Consumer { p = &buf; while (*) { while (p->flag) {}; p->data = newdata(); p->flag = true; p = p->next; } q = &buf; while (*) { while (!q->flag) {}; read = q->data; q->flag = false; q = q->next; } } data flag
Conclusions • The moral … – TAR can check concurrent software – w/o (really) exploring all interleavings • The devil … – Shared memory via pointers – Explicating local state • Need to track some local state of “other” threads – Approximate Counterexample analysis • Implemented TAR in BLAST – Race checking for drivers (each client is a thread) – Linux/Windows drivers 1 -10 Kloc • Looking for examples and properties …
BLAST Berkeley Lazy Abstraction Software * Tool www. eecs. berkeley. edu/~tah/blast/
Use Summaries (“Assume”) safe
Verify Summaries (“Guarantee”) µ safe
- Slides: 33