540 likes | 551 Views
This study focuses on utilizing BMC approach for sequentializing concurrent C programs to enable efficient reachability analysis, bug finding, and shared memory handling in a structured manner. Various BMC tools, like BLITZ, CBMC, and ESBMC, are explored for tackling the reachability problem, assertion failures, and concurrency issues in concurrent programs. The sequentialization process involves code-to-code translation, Bounded Program Solver, and Sequentialization tool development to target BMC analyses for scalability and simplicity.
E N D
On SequentializingConcurrent Programs (Bounded Model Checking) GennaroParlato University of Southampton, UK UPMARC 7th Summer School on Multicore Computing, June 8-10, 2015
Concurrent Programs - Reachability Problem concurrent C programs • POSIX threads • SC memory model reachability • assertion failure • out-of-bound array • division-by-zero, … bounded model checking (BMC) • bug-finding, not complete analysis SHARED MEMORY … T1 TN T2 THREADS
BMC approach – Sequential Programs Efficient tools for C • BLITZ [ Cho, D'Silva, Song – ASE’13 ] • CBMC [ Clarke, Kroening, Lerda – TACAS’04 ] • LLBMC [ Falke, Merz, Sinz – ASE’13 ] • ESBMC [ Cordeiro, Fischer, Marques-Silva – ASE’09 ] SAT/SMT FORMULA BOUNDED PROGRAM SOLVER PROGRAM inlining unrolling SSA form
BMC approach - Concurrent C Programs SAT/SMT FORMULA BOUNDED PROGRAM SOLVER CONC PROGRAM concurrency handling • direct SAT/SMT approach • encode each thread as in the sequential case • add a conjunct for shared memory operations • all possible interleavings in the bounded program φthreads∧ φconcurrency • papers: • [ Sinha, Wang – POPL’11 ] • [ Alglave, Kroening, Tautschnig – CAV’13 ]
Sequentialization CONC PROGRAM SAT/SMT FORMULA BOUNDED PROGRAM SOLVER SEQUENTIALIZATION (code-to-code translation) BMC SEQ TOOL SEQ PROGRAM • papers • proposal[ Qadeer, Wu – PLDI’04 ] • eager, bounded context-switch, finite # threads[ Lal, Reps – CAV’08 ] • lazy, finite # threads, parameterized • [La Torre, Madhusudan, Parlato – CAV’09, CAV’10] • thread creation[Bouajjani, Emmi, Parlato – SAS’11] • [Emmi, Qadeer, Rakamaric – POPL’11] • Lal/Reps for real-time systems[Chaki, Gurfinkel, Strichman – FMCAD’11] • message-passing programs[Bouajjani, Emmi -- TACAS’12]
Sequentialization CONC PROGRAM SAT/SMT FORMULA BOUNDED PROGRAM SOLVER SEQUENTIALIZATION (code-to-code translation) BMC SEQ TOOL SEQ PROGRAM • BMC-based tools (Implementations of variants of Lal/Reps schema) • Corral (microsoft research) [ Lal, Qadeer, Lahiri – CAV’12, FSE’14 ] • Rek[ Chaki, Gurfinkel, Strichman – FMCAD’11 ] • STORM[ Lahiri,Qadeer,Rakamaric CAV’09 ]
Sequentialization CONC PROGRAM SAT/SMT FORMULA BOUNDED PROGRAM SOLVER SEQUENTIALIZATION (code-to-code translation) BMC SEQ TOOL SEQ PROGRAM • We have designed • new sequentializations targeting BMC • scalable analyses + surprisingly simple • Lazy-CSeq • Memory Unwinding
Lazy-CSeq: Schema Overview (new sequentialization for BMC) [ Inverso–Tomasco–Fischer–La Torre–Parlato, CAV’14 ]
Lazy-CSeq Approach CONC PROGRAM BOUNDED PROGRAM SEQUENTIALIZATION (code-to-code translation) BMC SEQUENTIAL TOOL SEQ PROGRAM
Bounded Concurrent Programs main() T0 … T1 TN-1 TN • no loops • no function calls • Control flow only forward • one procedure for each thread
Round Robin Schedule round 1 round 2 round 3 main() T0 … T1 TN-1 TN round k • Lazy-Cseq sequentialization: • captures all bounded Round-Robin computations for a given bound • error manifest themselves within very few rounds • [ Musuvathi, Qadeer – PLDI’07 ]
Schema Overview … bounded concurrent program main() T0 TN T1 Sequentialization (code-to-code translation) … translates translates translates “equivalent” Sequential program with non determinism … F0 FN main() F1 Sequentialized functions Driver
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION • a global pc for each thread • thread locals thread global main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); }
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION • for each round • for each thread Ti • simulate Ti main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); }
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(0); stmt0; 2: CS(1); stmt1; 3: CS(2);stmt2; .. . E XE . .. M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); }
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION Fi() switch(pci) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1:CS(0);stmt0; 2: CS(1);stmt1; 3: CS(2);stmt2; .. . E XE . .. M: CS(M);stmtM; main driver context-switch resume mechanism pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); } ... ...
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(1);stmt0; 2: CS(2);stmt1; 3: CS(3); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); } ... ... ... Context-switch simulation: #define CS(j) if (*) { pci=j; return; }
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(1);stmt0; 2: CS(2);stmt1; 3: CS(3); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); } ... ... ...
Naïve Lazy Sequentialization: CROSS PRODUCT SIMULATION switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(0);stmt0; 2: CS(1);stmt1; 3: CS(2); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver Formula encoding: goto statement to formula add a guard for each crossing control-flow edge = O(M2) guards pc0=0; pc1=0; ... pcN=0; local0; local1; ... localk; main() { for (r=0; r<R; r++) for(k=0; k<N; k++) // simulate Tk Fk(); } ... ... ...
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(0);stmt0; 2: CS(1);stmt1; 3: CS(2); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); } ... ... ...
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(0);stmt0; 2: CS(1);stmt1; 3: CS(2); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); } ... ... ...
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(0);stmt0; 2: CS(1);stmt1; 3: CS(2); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; main() { for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) Fi(); } ...
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(1);stmt0; 2: CS(2);stmt1; 3: CS(3); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; nextCS; main() for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) nextCS = nondet; assume(nextCS>=pci) Fi(); pci = nextCS; ... #define CS(j) NEW if (j<pci || j>=nextCS) goto j+1;
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(1);stmt0; 2: CS(2);stmt1; 3: CS(3); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; nextCS; main() for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) nextCS = nondet; assume(nextCS>=pci) Fi(); pci = nextCS; #define CS(j) NEW if (j<pci || j>=nextCS) goto j+1;
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION Fi() switch(pck) { case 0: goto 0; case 1: goto 1; case 2: goto 2; ... case M: goto M; } 1: CS(0);stmt0; 2: CS(1);stmt1; 3: CS(2); stmt2; .. . E XE . . . M: CS(M);stmtM; main driver pc0=0; ... pcN=0; local0; ... localk; nextCS; main() for (r=0; r<K; r++) for (i=0; i<N; i++) // simulate Ti if (activei) nextCS = nondet; assume(nextCS>=pci) Fi(); pci = nextCS; ... #define CS(j) NEW if (j<pci || j>=nextCS) goto j+1;
CSeq-Lazy Sequentialization:CROSS PRODUCT SIMULATION resuming + context-switch 1: CS(1); stmt1; 2: CS(2); stmt2; 3: CS(3); stmt3; EXECUTE M: CS(M); stmtM; skip ... pci nextCS skip ... #define CS(j)NEW if (j<pci || j>=nextCS) goto j+1;
Individual Threads Sequentialization 1: CS(1); stmt1; 2: CS(2); stmt2; 3: CS(3); stmt3; EXECUTE M: CS(M); stmtM; Formula encoding: goto statement to formula add a guard for each crossing control-flow edge = O(M) guards ... ...
Individual Threads Sequentialization 1: CS(1); stmt1; 2: CS(2); stmt2; 3: CS(3); stmt3; EXECUTE M: CS(M); stmtM; ... • inject light-weight, non-invasive control code • no non-determinism • no assignments • no return ... #define CS(j) if (j<pci || j>=next_CS) goto pc+1;
Evaluation: bug-huntingSVCOMP’14, Concurrency (UNSAFE instances)
Evaluation: bug-huntingSVCOMP’14, Concurrency (UNSAFE instances)
Remarks Lazy-CSeq is a new sequentialization targeted to BMC backends • lazy • based on bounded round-robin computations • efficient for bug-hunting • simple to implement (CSeq framework) Eager vs Lazy • no empirical evidence that lazy is faster • Lazy does not require an implementation of a memory model and handling of error checks (as opposed to LR sequentialization) Lazy-CSeq won 2gold medals in the Concurrency category of the last two editions of the software verification competition SV-COMP • all verification tasks solved • 30x faster than the best tool with native concurrency handling
Sequentialization based on Memory Unwinding [ Tomasco–Inverso–Fischer–La Torre–Parlato, TACAS’15 ]
Interleaving semantics T1 T2 T3 run wx wy rx wy rx ry wx wx rx ry wy rx
From interleavings to memory unwinding T1 T2 T3 run wx wy rx wy rx ry wx wx rx ry wy rx accesses to sharedmemory wx wy rx wy rx ry wx rx ry wy rx MEMORY UNWINDING wx wy writes wx wy wy wx wy wy wx wy ⇒ Memory unwinding models thread interaction history by data
Memory unwindings as thread interfaces • bounding parameter: # of shared write operations • most concurrency bugs exposed by few interactions [Lu, Park, Seo, Zhou – ASPLOS 2008] TASK 2 TASK 1 MEMORY UNWINDING T1 T2 T1 Assume-guarantee reasoning style decomposition of verification
Assume-guarantee reasoning TASK 2 MEMORY UNWINDING TASK 1 T1 T2 Task 1: Task 2: Thread T1 assumes: MU writes of other threads guarantees: its own MU writes Thread T2 assumes: MU writes of other threads guarantees: its own MU writes
Simulating a thread against an MU Global variable write – check against current MU entry void write(uint t, uint v, intval) { mc[t] = th_nxt_wr[t][mc[t]]; assume(var[mc[t]] == v); assume(val[mc[t]] == val); } void write(uint t, uint v, intval) { mc[t] = th_nxt_wr[t][mc[t]]; } y=1 …. a=x+5 ..... x=1 …. mc mc pc pc x=2 y=1 y=1 z=2 x=3 y=8 x=1
Simulating a thread against an MU Local statement (no global variables) – update pc, keep mc y=1 …. a=x+5 ..... x=1 …. mc pc pc x=2 y=1 y=1 z=2 x=3 y=8 x=1
Simulating a thread against an MU Global variable read – “pick write position in range” non-deterministic assignment int read(uint t, uint v) { if (thr_terminated()) return 0; if (var_fst_wr[v]==0) return 0; uintr_from = *; assume((r_from <= last_wr_pos) &&(r_from< th_nxt_wr[t][mc[t]])); assume(var[r_from] == v); if (r_from< mc[t]) assume(var_nxt_wr[r_from] > mc[t]); else { if (r_from < var_fst_wr[v]) return 0; mc[t] = r_from; }; return value[r_from]; } y=1 …. a=x+5 ..... x=1 …. mc pc x=2 y=1 y=1 z=2 x=3 y=8 x=1
Simulating a thread against an MU Global variable read – “pick write position in range” Check in unwinding... ...before next write of thread... int read(uint t, uint v) { if (thr_terminated()) return 0; if (var_fst_wr[v]==0) return 0; uintr_from= *; assume((r_from <= last_wr_pos) &&(r_from< th_nxt_wr[t][mc[t]])); assume(var[r_from] == v); if (r_from< mc[t]) assume(var_nxt_wr[r_from] > mc[t]); else { if (r_from < var_fst_wr[v]) return 0; mc[t] = r_from; }; return value[r_from]; } y=1 …. a=x+5 ..... x=1 …. mc pc x=2 y=1 y=1 z=2 x=3 y=8 x=1
Simulating a thread against an MU Global variable read – “pick write position in range” ...before next write of thread... int read(uint t, uint v) { if (thr_terminated()) return 0; if (var_fst_wr[v]==0) return 0; uintr_from= *; assume((r_from <= last_wr_pos) && (r_from< th_nxt_wr[t][mc[t]])); assume(var[r_from] == v); if (r_from< mc[t]) assume(var_nxt_wr[nxt_mc] > mc[t]); else { if (r_from< var_fst_wr[v]) return 0; mc[t] = r_from; }; return value[r_from]; } y=1 …. a=x+5 ..... x=1 …. mc pc x=2 y=1 y=1 z=2 x=3 y=8 ...right variable... x=1
Simulating a thread against an MU Global variable read – “pick write position in range” int read(uint t, uint v) { if (thr_terminated()) return 0; if (var_fst_wr[v]==0) return 0; uintr_from= *; assume((r_from <= last_wr_pos) &&(r_from< th_nxt_wr[t][mc[t]])); assume(var[r_from] == v); if (r_from< mc[t]) assume(var_nxt_wr[r_from] > mc[t]); else { if (r_from < var_fst_wr[v]) return 0; mc[t] = r_from; }; return value[r_from]; } y=1 …. a=x+5 ..... x=1 …. mc pc x=2 y=1 y=1 z=2 x=3 y=8 ...right variable... x=1 ...next write of variable in future...
Simulating a thread against an MU Global variable read – “pick write position in range” int read(uint t, uint v) { if (thr_terminated()) return 0; if (var_fst_wr[v]==0) return 0; uintr_from = *; assume((r_from <= last_wr_pos) &&(r_from< th_nxt_wr[t][mc[t]])); assume(var[r_from] == v); if (r_from< mc[t]) assume(var_nxt_wr[r_from] > mc[t]); else { if (r_from < var_fst_wr[v]) return 0; mc[t] = r_from; }; return value[r_from]; } y=1 …. a=x+5 ..... x=1 …. mc mc pc pc x=2 y=1 y=1 z=2 x=3 y=8 x=1 ...before next write of variable... ...else update mc. Return value.
Simulating a thread against an MU Handling errors – “flag that an error occurs” y=1 …. a=x+5 ..... x=1 …. assert(b); pc x=2 y=1 y=1 z=2 x=3 y=8 x=1 translates to if (! b) _error = 1; (computation might not be feasible)
Our apporach: sequentialization by MU … Concurrent program T0 TN T1 Sequentialization (code-to-code translation) … translates translates translates MEMORY UNWINDING Sequential program … F0 FN main() F1 Simulation functions
Main Sequential Program Thread creation main void main(void){ memory_init(W); ct := mem_thread_create(); F0(); thread_terminate(ct); all_thread_simulated(); assert(_error == 0) } instantiate memory unwinding register main thread simulation starts from main thread all writes of the thread are executed all writes of the MU are executed error check
CSeq framework • is a framework that simplifies code-to-code translations • for C programs + Pthread • comprises several code-to-code translation modules • supports several sequential analysis back-end tools • Internal modules • unrolling • function inlining • counter-example • … testing Klee sequential non-deterministic C program concurrent C program • bounded • model-checking • BLITZ • CBMC • ESBMC • LLBM sequential analysis tool code-to-code translation P P' • abstraction • CPA-checker • Frama-C • SATABS • Sequentialisations • Memory-Unwinding • Lazy-CSeq • LR-CSeq • … http://users.ecs.soton.ac.uk/gp4/cseq/
Evaluation[ SV–COMP 2014 2015 ] • SV-COMP software verification competition @ TACAS • Concurrency category benchmarks: 1003 files • Lazy-CSeq won 2 GOLD medals (2014, 2015) • MU-CSeq won 2 SILVER medals (2014, 2015)