Outline Temporal Safety Proofs for Systems Code Dennis Peters Christian-Albrechts-Universität Kiel Department of Computer Science and Applied Mathematics Zuverlässigkeit von Software in sicherheitskritischen Systemen SS 2005 Temporal Safety Proofs for Systems Code Slide 1 Outline Outline 1 Introduction Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method 2 An Example Locking Problem Verification Certification 3 Summary SS 2005 Temporal Safety Proofs for Systems Code Slide 2 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Correct And Trusted Software One important aim of software engineering: Simplify the construction of correct and trusted software. Therefore we need technologies for the verification and the certification of software. They are most effective when they work directly for the actual code. Separately constructed abstract models can contain modelling errors. SS 2005 Temporal Safety Proofs for Systems Code Slide 3 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Verification With Model-Checking For model-checking, we need ... the code of the software itself. a specification for the software. The model-checker verify the program with respect to the specification. Model-checking based approaches have got the following advantages: They are fully automatic unlike most theorem-proving approaches. They are capable of checking path-sensitive propereties unlike most program-analysis approaches. SS 2005 Temporal Safety Proofs for Systems Code Slide 4 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Traditional Approach [1] One traditional flow for model checking proceeds through the following loop (abstraction-verify-refine loop): Step 1 (”abstraction”) A finite set of predicates is chosen. An abstract model is built automatically as a finite push-down automata. The states represent truth assignments for the chosen predicates. Step 2 (”verification”) The abstract model is automatically checked for the desired properties. If the model is error-free: return ”program correct”. Otherwise: an abstract counterexample is produced automatically. SS 2005 Temporal Safety Proofs for Systems Code Slide 5 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Traditional Approach [2] Step 3 (”counterexample-driven refinement”) It is checked automatically if the abstract counterexample corresponds to an concrete counterexample. If so: return ”program incorrect”. Otherwise: add new predicates. Goto Step 1 SS 2005 Temporal Safety Proofs for Systems Code Slide 6 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Traditional Approach [3] Problems with this approach: Step 1 and Step 2 are computationally hard problems. Without additional optimizations, the method does not scale to large systems. One approach to optimize the three phases: the concept of lazy abstraction. SS 2005 Temporal Safety Proofs for Systems Code Slide 7 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method The Lazy Abstraction Method In this method, we represent programs as control flow automata (CFA). go to The lazy abstraction method contains of two steps: Forward search Build a depth-first order search tree. Look if an error node is reachable. If an error node is reachable, go to step 2, else the program is correct. Backwards counterexample analysis Checks if it is an real error or results from an abstraction that is too coarse. Find the first node in the search tree from the error node on where the abstraction trace fails. If such a node exists, then refine the abstraction from this node on and start the forward search, else it is a real error. SS 2005 Temporal Safety Proofs for Systems Code Slide 8 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Advantages with this Approach Lazy abstraction ... short-circuits the traditional abstract-verify-refine loop. avoids the repetition of work in successive abstraction phases and in successive model-checking phases. SS 2005 Temporal Safety Proofs for Systems Code Slide 9 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method Certification To witness that the behavior of an untrusted code is correct, we use proof-carrying codes(PCC). The producer sends the consumer the code annoted with loop invariants and function pre- and postconditions and a proof of correcness of a verification condition. The consumer builds with the code and the annotations the verification condition and checks if the proof is correct. It is easier to check the correctness of the proof then to construct the proof. Disadvantage: The user may give annotations like loop invariants. SS 2005 Temporal Safety Proofs for Systems Code Slide 10 Introduction An Example Summary Correct And Trusted Software A Traditional Model-Checking Approach The Lazy Abstraction Method BLAST The Berkeley Lazy Abstraction Software verification Tool (BLAST) is an attempt to improve the abstract-verfy-refine loop by the use of lazy abstraction on C programs. The developers of BLAST focuse on the automatic verification and certification of device drivers. They are written in very low level, but must meet very high level specifications. They have tested linux and windows drivers up tp 60K lines. They could discover several errors and produce fully automatic small proofs of correctness less then 150K. SS 2005 Temporal Safety Proofs for Systems Code Slide 11 Introduction An Example Summary Locking Problem Verification Certification An Example Consider following C program: The program Example Example() { if (*){ do{ got lock = 0; 8: if (*){ 9: lock(); got lock++; } 10: if (got lock){ 11: unlock(); } 12: } while (*) } 1: 7: SS 2005 2: 3: 4: 5: 6: do { lock(); old = new; if(*) { unlock(); new++; } } while (new != old); unlock(); return; } Temporal Safety Proofs for Systems Code Slide 12 Introduction An Example Summary Locking Problem Verification Certification Locking Specification The locking discipline is specified by the following automata: unlock() LOCK=0 LOCK=1 lock() unlock() lock() ERR SS 2005 Temporal Safety Proofs for Systems Code Slide 13 Introduction An Example Summary Locking Problem Verification Certification The program and the specification are inputs to BLAST as C code. They are then combined to a single sequential program with a special error location. The program does not meet the specification if the error location is reachable. SS 2005 Temporal Safety Proofs for Systems Code Slide 14 Locking Problem Verification Certification Introduction An Example Summary Control-Flow Automata (CFA) 1 [true] [true] 7 2 lock() new = old [true] [true] 3 8 A CFA is a directed graph. [true] 9 4 unlock() new++ We present the C program as CFA. got_lock = 0 5 [new != old] lock() got_lock++ 10 [got_lock != 0] [new = old] [got_lock = 0] 11 6 unlock() 12 ret Vertices corresponds to control points of the program. Edges correspondends to program operations. unlock() [true] [true] go back SS 2005 Temporal Safety Proofs for Systems Code Slide 15 Introduction An Example Summary Locking Problem Verification Certification Verification The model checking is done on the CFA of the C code. The reachability problem for CFAs is undecidable, so the lazy abstraction may not terminate on all inputs. From the locking specification we know, that for example an error occurs when the function lock() is called as long LOCK is 1. So we consider the two predicates LOCK = 1 and LOCK = 0. SS 2005 Temporal Safety Proofs for Systems Code Slide 16 Introduction An Example Summary Locking Problem Verification Certification Forward Search [true] 1 2 LOCK = 0 lock() new = old 3 LOCK = 1 [true] 4 LOCK = 1 unlock() new++ 5 LOCK = 0 [new = old] 6 LOCK = 0 unlock() ERR LOCK = 0 LOCK = 0 The forward search builds an search tree in depth-first order. The nodes of the tree correspondends to the nodes of the CFA. The labels of the nodes are formulas, called the reachable regions. They are obtained from the reachable regions of the parents node and the instructions on the edge between the two nodes. The reachable regions contain of a finite set of the considered predicates and are described as a boolean combination of these predicates. SS 2005 Temporal Safety Proofs for Systems Code Slide 17 Introduction An Example Summary Locking Problem Verification Certification Backwards Counterexample Analysis [1] Here we check if the counterexample is a real one. Therefore we simulate the error trace backwards. If we find a node where the abstract trace fails then we call this node a pivot node. Then we have to refine the abstraction form the pivot node on. Otherwise we have found a real error in the program. SS 2005 Temporal Safety Proofs for Systems Code Slide 18 Introduction An Example Summary Locking Problem Verification Certification Backwards Counterexample Analysis [2] [true] 1 2 LOCK = 0 LOCK = 0 {LOCK = 0 & new + 1 = new} lock() new = old 3 LOCK = 1 {LOCK = 1 & new + 1 = old} [true] 4 LOCK = 1 {LOCK = 1 & new + 1 = old} unlock() new++ 5 LOCK = 0 LOCK = 0 So this is the pivot node and we refine our search with the predicate new = old from node 2 on. {LOCK = 0} unlock() ERR They represent the set of states which can reach an error state from the corresponding location by executing the instructions on the edge. The bad region of node 2 is unsatisfiable. {LOCK = 0 & new = old} [new = old] 6 The formula in the curly braces are called the bad regions. LOCK = 0 SS 2005 Temporal Safety Proofs for Systems Code Slide 19 Introduction An Example Summary Locking Problem Verification Certification Search with New Predicates 1 [true] LOCK = 0 LOCK = 0 Now we continue the forward search from the pivot node on. 2 We use the additional predicate new = old. 3 LOCK = 1 & new = old [true] LOCK = 1 & new = old 4 LOCK = 0 & !new = old 5 [true] 5 2 LOCK = 0 & new = old LOCK = 0 & !new = old [new = old] [new != old] 6 false 2 6 false Notice, that we can stop at the leaf labled with 2, because the reachable region LOCK = 0 ∧ new = old is a subset of LOCK = 0. LOCK = 0 Such a node is called covered. unlock() Obviously there is no error state reachable in the left subtree. ret LOCK = 0 & new = old SS 2005 Temporal Safety Proofs for Systems Code Slide 20 Introduction An Example Summary Locking Problem Verification Certification Right Subtree[1] The forward search and the backwards counterexample analysis results that 7 is a pivot node. So we have to add the new predicate got lock = 0 and then restart the search. SS 2005 Temporal Safety Proofs for Systems Code Slide 21 Introduction An Example Summary Locking Problem Verification Certification Right Subtree[2] Because the leaves are covered, the search terminates. So, the program is correct! SS 2005 Temporal Safety Proofs for Systems Code Slide 22 Introduction An Example Summary Locking Problem Verification Certification Savings Each part of the state space is refined as much as required. We explore only the portion of of state space that is required in order to prove correctness and do not throw away the work done earlier. SS 2005 Temporal Safety Proofs for Systems Code Slide 23 Introduction An Example Summary Locking Problem Verification Certification Certification For certifiction we use a standard temporal-safety rule. Temporal-Safety rule Given a transition system. If we can find a set I of states such that (1) I contains all initial states, (2) I contains no error states, (3) I is closed under succesor states, then the system cannot reach an error state from an initial state. If (1)-(3) are satisfied, then I is called an invariant set. SS 2005 Temporal Safety Proofs for Systems Code Slide 24 Introduction An Example Summary Locking Problem Verification Certification Proof of Correctness [1] In our setting, we got for each vertex q of the CFA a location invariant I(q) such that 1. (LOCK = 0) ⇒ I(1), 2. I(ERR) = false, 3. for each pair of CFA vertices q and q 0 with an edge labeled op between them, sp(I(q), op) ⇒ I(q 0 ), where sp is the strongest postcondition operator. To provide a proof of correctnes we have to give a location invariant for each vertex of the CFA and proof that they meet the three requirements. A location invariant I(q) is the disjunction of all reachable regions that corresponds to q in the search tree. For example, I(5) = (LOCK = 0 ∧ ¬new = old) ∨ (LOCK = 1 ∧ new = old) SS 2005 Temporal Safety Proofs for Systems Code Slide 25 Introduction An Example Summary Locking Problem Verification Certification Proof of Correctness [2] 1. It is easy to check that (LOCK = 0) ⇒ I(1) is valid, because the root is labeled with this condition and it is the precondition. of the program. 2. I(ERR) = false is valid, because there is no error state in the search tree. op 3. We have to check, that for each edge q −→ q 0 of the CFA, sp(I(q), op) ⇒ I(q 0 ) is valid. Consider, sp((LOCK = 0∧¬new = old)∨(LOCK = 1∧new = old), [new!=old]) ⇒ (LOCK = 0) By distributing the sp operator, we get ((LOCK = 0) ∨ false) ⇒ (LOCK = 0) Obviously the formulas (LOCK = 0) ⇒ (LOCK = 0) and false ⇒ (LOCK = 0) are valid. SS 2005 Temporal Safety Proofs for Systems Code Slide 26 Introduction An Example Summary Locking Problem Verification Certification The Proof of the Verification Condition In general we have to prove the following verification condition: ^ (Pre ⇒ I(q0 ))∧(I(qε ) = false)∧ (sp(I(q), op) ⇒ I(q 0 )) op q −→q 0 where q0 is the initial state and qε the error state. As we could see in the example, we can break the proof of 3. into simpler proofs. These proofs are available in the forward-search of the model checker. We reduce the size of the proof because we only use the predicates that are essential for the proof. The proof of the verification condition is encoded in LF (Logical Framework), so that the proof-checking reduces to a linear-time type-checking problem. SS 2005 Temporal Safety Proofs for Systems Code Slide 27 Introduction An Example Summary Summary Lazy abstraction is a semi-algorithm that optimizes the traditional abstract-verify-refine loop : LA considers only the reachable part of the entire state space. It uses only predicates where they are required. The parts that are known to be error free are not searched again. With this optimations we can produce small proofs of correctness. So we can produce small proof certificates. By encoding the proof, proof checking becomes a type-checking problem. SS 2005 Temporal Safety Proofs for Systems Code Slide 28 Appendix 4 Locking Specification in BLAST Verification Times with BLAST Appendix Locking Specification in BLAST Verification Times with BLAST SS 2005 Temporal Safety Proofs for Systems Code Slide 29 Appendix Locking Specification in BLAST Verification Times with BLAST Locking Specification in BLAST global int locked = 0; . event { pattern { init(); } action { locked = 0; } } event { pattern { lock(); } guard { locked == 0 } action { locked = 1; } } event { pattern { unlock(); } guard { locked == 1 } action { locked = 0; } } SS 2005 Temporal Safety Proofs for Systems Code Slide 30 Appendix Locking Specification in BLAST Verification Times with BLAST Verification Times with BLAST Program qpmouse.c ide.c aha152x.c tlan.c caudio.c floppy.c [fixed] kbfilter.c Postprocessed LOC 23539 18131 17736 16506 Predicates Total Actice 2 2 5 5 2 2 5 4 17798 17386 BLAST Time (sec) 0.50 4.59 20.93 428.63 Ctrex analysis (sec) 0.00 0.01 0.00 403.33 85 45 1398.62 540.96 62 37 2086.35 1565.34 93 44 395.97 17.46 12131 54 40 64.16 5.89 48 35 256.92 165.25 [fixed] 37 34 10.00 0.38 mouclass.c 17372 57 46 54.46 3.34 parport.c 61781 193 50 1980.09 519.69 Table 1. Verification times with BLAST. A blank proof size indicates a bug was found. SS 2005 Proof Size (bytes) 175 253 405 156787 60129 7619 102967 Temporal Safety Proofs for Systems Code Slide 31