Runtime Errors Proving Non-Termination Ashutosh K. Gupta Thomas A. Henzinger Rupak Majumdar MPI-SWS EPFL UCLA Andrey Rybalchenko Ru-Gang Xu MPI-SWS UCLA 2 Non-Termination Errors Proving Non-Termination • Search for infinite executions …. • Needs effective (finitary) representation .. Failed Termination Proof vs. Non- TNT: Testing Non-Termination Termination • Successful termination provers: Tool for proving non-termination PolyRank, Terminator, ACL2, TerminWeb, AProVE, … • Inherently incomplete algorithms Input leading to C program • Failed termination proof ≠ non-termination TNT non-termination Don’t know Proved Proved Non- Proved Non- Terminating Terminating Terminating Terminating Non-Terminating Terminating Non-Terminating
Outline Example: Non-termination Error • Input to TNT: – Mondriaan memory protection system • Example: • (early version courtesy: E. Witchel) – Uses recursion for basic operation – Non-termination error in a memory protection system – Termination required • TNT algorithm: • Output by TNT: – Lasso search – non-termination bug (now fixed) in _mmpt_insert procedure: – Recurrent set computation cyclic sequences of calls to _mmpt_insert ( … , 0, 3, … …, TAB_ROOT , 0, … ) Non-Termination in _mmpt_insert Non-Termination in _mmpt_insert (first call) void _mmpt_insert (struct* mmpt, base, len, prot, tab_t* tab, level, … ) { void _mmpt_insert (struct* mmpt, 0, 3, prot, TAB_ROOT , 0, … ) { if(len == 0) return; // Exit condition if(len == 0) return; // Exit condition int idx = make_idx(mmpt, base, level); int idx = make_idx(mmpt, base, level); if(level < 2 && … … … && len >= tab_len(mmpt, level + 1)) { if(level < 2 && … … … && len >= tab_len(mmpt, level + 1)) { … … … … … … } else if(level < 2 && tab[idx] && !uentry_is_data(mmpt, tab[idx])) { } else if(level < 2 && tab[idx] && !uentry_is_data(mmpt, tab[idx])) { _mmpt_insert (mmpt, base, len, prot, (tab_t*)tab[idx], level + 1, …); _mmpt_insert (mmpt, base, len, prot, (tab_t*)tab[idx], level + 1, …); } else if(level < 2 && … … … ) { } else if(level < 2 && … … … ) { … … … … … … } else { } else { for(; len >= subblock_len(mmpt, level) && … … … ; … … … ) { for(; len >= subblock_len(mmpt, level) && … … … ; … … … ) { … … … … … … } } _mmpt_insert (mmpt, base, len, prot, mmpt->tab, 0, …); _mmpt_insert (mmpt, base, len, prot, mmpt->tab, 0, …); } } } } Non-Termination in _mmpt_insert Non-Termination in _mmpt_insert (first call) (second call) void _mmpt_insert (struct* mmpt, 0, 3, prot, TAB_ROOT , 0, … ) { void _mmpt_insert (struct* mmpt, 0, 3, prot, TAB_MID , 1, … ) { if( 3 == 0) return; // Exit condition if(len == 0) return; // Exit condition int 0 = make_idx(mmpt, 0, 0); int idx = make_idx(mmpt, base, level); if( 0 < 2 && … … … && 3 >= 4MB ) { if(level < 2 && … … … && len >= tab_len(mmpt, level + 1)) { … … … … … … } else if( 0 < 2 && tab[ 0 ] && !uentry_is_data(mmpt, tab[ 0 ])) { } else if(level < 2 && tab[idx] && !uentry_is_data(mmpt, tab[idx])) { _mmpt_insert (mmpt, 0, 3, prot, (tab_t*)tab[ 0 ], 0 + 1, …); _mmpt_insert (mmpt, base, len, prot, (tab_t*)tab[idx], level + 1, …); } else if(level < 2 && … … … ) { } else if(level < 2 && … … … ) { … … … … … … } else { } else { for(; len >= subblock_len(mmpt, level) && … … … ; … … … ) { for(; len >= subblock_len(mmpt, level) && … … … ; … … … ) { … … … … … … } } _mmpt_insert (mmpt, base, len, prot, mmpt->tab, 0, …); _mmpt_insert (mmpt, base, len, prot, mmpt->tab, 0, …); } } } }
Non-Termination in _mmpt_insert Non-Termination in _mmpt_insert (second call) (third call) void _mmpt_insert (struct* mmpt, 0, 3, prot, TAB_MID , 1, … ) { void _mmpt_insert (struct* mmpt, 0, 3, prot, TAB_LEAF , 2, … ) { if( 3 == 0) return; // Exit condition if(len == 0) return; // Exit condition int 0 = make_idx(mmpt, 0, 1); int idx = make_idx(mmpt, base, level); if( 1 < 2 && … … … && 3 >= 4KB ) { if( level < 2 && … … … && len >= tab_len(mmpt, level + 1)) { … … … … … … } else if( 1 < 2 && tab[ 0 ] && !uentry_is_data(mmpt, tab[ 0 ])) { } else if( level < 2 && tab[idx] && !uentry_is_data(mmpt, tab[idx])) { _mmpt_insert (mmpt, 0, 3, prot, (tab_t*)tab[ 0 ], 1 + 1, …); _mmpt_insert (mmpt, base, len, prot, (tab_t*)tab[idx], level + 1, …); } else if(level < 2 && … … … ) { } else if( level < 2 && … … … ) { … … … … … … } else { } else { for(; len >= subblock_len(mmpt, level) && … … … ; … … … ) { for(; len >= subblock_len(mmpt, level) && … … … ; … … … ) { … … … … … … } } _mmpt_insert (mmpt, base, len, prot, mmpt->tab, 0, …); _mmpt_insert (mmpt, base, len, prot, mmpt->tab, 0, …); } } } } Non-Termination in _mmpt_insert What does TNT do? (third call) void _mmpt_insert (struct* mmpt, 0, 3, prot, TAB_LEAF , 2, … ) { if( 3 == 0) return; // Exit condition • TNT finds a cyclic sequence of calls to _mmpt_insert int 0 = make_idx(mmpt, 0, 2); if( 2 < 2 && … … … && 3 >= tab_len(mmpt, 2 + 1)) { • The sequence is lasso – shaped … … … } else if( 2 < 2 && tab[idx] && !uentry_is_data(mmpt, tab[idx])) { level=0 level=1 level=2 level=0 _mmpt_insert (mmpt, base, len, prot, (tab_t*)tab[idx], level + 1, …); } else if( 2 < 2 && … … … ) { … … … Recursive call sequence } else { for(; 3 >= 4 && … … … ; … … … ) { • Non-termination is proved by analyzing the lasso Same … … … – same valuation of input parameters after the call cycle Parameters as } we started _mmpt_insert (mmpt, 0, 3, prot, TAB_ROOT , 0, …); } } Paths and Executions Outline: a: x = 0; x=0 b: while(x>=0){ c: x=x + 1; x=1 x=2 x=3 } • Search for lassos a: x = 0; x=0 • Prove a lasso is non-terminating through recurrent sets c: x=x + 1; x=1 c: x=x + 1; x=2 c: x=x + 1; x=3 Path = seq. of statements Execution = seq. of states
Lassos What do infinite paths look like? Stem • Pair of paths: (stem, loop) • General paths: • Represents infinite periodic path: e.g. stmtA Loop a: stmtA stem(loop) ω stmtB 2 stmtC 3 stmtB 5 stmtC 7 … while( … ){ Stem a if( … ) a: x = 0; • Periodic paths or Lassos: x=0; x=0; b: stmtB b: while(x>=0){ e.g. stmtA stmtB stmtC stmtB stmtC b else c: x=x + 1; … c: stmtC } Loop assume(x>=0); assume(x>=0); x=x+1; } c c x=x+1; stmtA(stmtB stmtC) ω • Lasso = ( x=0; , assume(x>=0) ; x=x+1; ) • TNT only considers periodic paths TNT Algorithm Lasso Search • Find lassos by symbolic execution • Implementation similar to DART, CUTE, SAGE, … • Two-step algorithm: 1. Search for feasible lassos Stem • Uses symbolic execution … … a: while( … ){ • Quickly find candidates for non-termination b: while( … ){ … … Loo } 2. Check each lasso for non-termination p } • Uses SAT and constraint solving … • Precise reasoning on small program fragments Recurrent Sets Outline: • Proves non-termination using inductive argument • Set of states RecSet is recurrent for relation ρ (x, x’) if: • Lasso search – non-empty � – some successor RecSet � of each state is in RecSet • Recurrent set computation � Theorem: ρ (x, x’) is non-terminating iff there exists RecSet . 24
Recommend
More recommend