• No results found

Verifying Temporal Properties Using Deductive Verifiers

N/A
N/A
Protected

Academic year: 2021

Share "Verifying Temporal Properties Using Deductive Verifiers"

Copied!
42
0
0

Loading.... (view fulltext now)

Full text

(1)

IN

DEGREE PROJECT TECHNOLOGY, FIRST CYCLE, 15 CREDITS

,

STOCKHOLM SWEDEN 2019

Verifying temporal properties of C

programs using deductive verifiers

JESPER AMILON

AXEL LINDEBERG

KTH ROYAL INSTITUTE OF TECHNOLOGY

(2)
(3)

Verifying Temporal Properties

Using Deductive Verifiers

JESPER AMILON, AXEL LINDEBERG

Bachelor in Computer Science Date: June 7, 2019

Supervisor: Dilian Gurov Examiner: Pawel Herman

School of Electrical Engineering and Computer Science

Swedish title: Verifiering av temporala egenskaper med hjälp av deduktiva verifierare

(4)
(5)

iii

Abstract

Formal verification is an area of theoretical computer science where mathe-matical logic is used to prove that a program behaves in a certain way. With the methods in formal verification, you can prove that the program follows some given specification and thereby behaves in the desired way. The area is largely split up into two distinct parts. One deals with how the program transforms data. This uses Hoare logic and deductive verification to prove that the program follows a given specification. The other part deals with tem-poral properties of the program, this uses temtem-poral logic and model checkers. The two areas are today largely separated. This report builds on a framework by Alur and Chaudhuri [1] which proves temporal properties in a Hoare logic style reasoning. By using this framework, this report aims to check the viabil-ity of using it with deductive verifiers. Thereby bridging the gap between the two areas for formal verification.

In conclusion, the report finds that it is certainly possible to prove tem-poral properties for C programs using Alur and Chaudhuri’s framework with deductive verifiers. In practical terms, though, it requires too much work to be feasible to use this framework by manually creating annotations for the de-ductive verifiers. In a small example program of 13 lines, proving a temporal property required around 50 extra lines of annotations. However, some parts of the annotation process could be automated with tooling support but to achieve full automation is probably not possible. This is partly due to ranking func-tions that the framework requires which, in general, are not easy to generate automatically.

(6)

iv

Sammanfattning

Formell verifikation är ett område inom teoretisk datalogi där man genom ma-tematisk logik vill bevisa att ett program beter sig på ett visst sätt. Genom detta kan man bevisa att programmet följer en given specifikation och beter sig som man tänkt. Detta område kan delas upp i två separata delar. Den ena hand-lar om att visa hur programmet transformerar data och bygger på Hoare logik och deduktiv verifikation för att bevisa att programmet följer en viss specifi-kation. Den andra delen handlar om temporala egenskaper och använder sig av model checking och temporal logik. Dessa delar behandlas vanligtvis sepa-rat från varandra. Denna rapport bygger på ett logiskt system utvecklat av Alur och Chaudhuri [1] som används för att bevisa temporala egenskaper genom ett angreppssätt som använder Hoare-logik. Genom att använda detta system vill denna rapport undersöka huruvida det är möjligt att bevisa temporala egenska-per med hjälp av deduktiva verifierare och därigenom minska klyftan mellan dessa två områden av formel verifikation.

Rapporten finner att det är möjligt att använda Alur och Chaudhuri’s sy-stem för att bevisa temporala egenskaper genom deduktiva verifierare. I prak-tiska termer krävs det för mycket arbete för att kunna användas genom att ma-nuellt skapa instruktioner till den deduktiva verifieraren. I ett litet exempelpro-gram på 13 rader kräves 50 extra rader av annotationer för att bevisa en viss temporal egenskap. Stora delar av den processen skulle dock kunna automa-tiseras med verktygsstöd. Att automatisera hela processen är dock antagligen inte möjligt. Detta är dels pågrund av att de rankning funktioner som systemet använder sig av är svåra att automatisera.

(7)

Contents

1 Introduction 1 1.1 Research Question . . . 3 2 Background 5 2.1 Temporal Logic . . . 5 2.2 Nested words . . . 6

2.3 A framework for temporal logic . . . 8

2.4 Tools for formal verification . . . 12

2.4.1 Deductive verifiers . . . 13 2.4.2 Model Checkers . . . 14 2.4.3 SMT solvers . . . 15 2.5 The C language . . . 16 3 Method 17 4 Results 19 4.1 Rule L-Safe . . . 19 4.2 Rule L-Resp . . . 21 5 Discussion 24 6 Conclusion 26 Bibliography 27

A Full L-Resp example program 29

(8)
(9)

Chapter 1

Introduction

Writing software is hard. Bugs and edge cases of function inputs is some-thing even experienced programmers often miss. To combat errors in soft-ware, many different approaches exists such as unit testing, integration tests, and code reviews. All these techniques are heuristic in nature and can in gen-eral never guarantee that the program is faultless, only reduce the probability. Ideally one would like to prove that the program is correct in the sense that it follows some specification. This is an area of much research and is called formal verification. In formal verification, mathematical logic is used to prove program correctness. Formal verification is often divided into two distinct categories:

• Showing how the program transforms data. This is done through Hoare Logic and can be thought of as contracts where you prove that executing some piece of code will produce some result, given that a precondition is true. For example, if the function input x is greater than 0 the output will be 2x

. For this type of logic, verification is often done with tools that are called deductive verifiers. See 2.4.1 for a more detailed description of deductive verifiers.

• Showing temporal properties of the program. These are properties relat-ing to time. For example, all files that are opened are eventually closed. Verifying temporal properties is done with tools that are called model checkers. Model checkers create an abstract model of the program, of-ten as a state transition graph, and use the model to prove or disprove properties of the program. See 2.4.2 for a more detailed description of model checkers.

Today, a lot of systems and programs are written in a modular approach where

(10)

2 CHAPTER 1. INTRODUCTION

the code is divided into different modules or procedures that interact with each other. Take for instance a modern aircraft. It is controlled by many different computers where perhaps one module could be controlling the speed and an-other module listens for changes in the wind outside the aircraft. The latter module then has to talk to the former one if it detects any upcoming major changes that require the speed controller to react. Since the implications of failing software on an aircraft crash are severe, it is feasible to expect that formal verification of the software on the aircraft is a necessity.

Hoare Logic works well in modular scenarios. It is possible to create the contracts for each procedure or module which means the tools are often use-ful when proving properties for programs divided into different modules, as with the case of the aircraft example. However, Hoare logic cannot capture the reactive part of the aircraft example. Model checkers are instead good at showing the reactive property, as well as other temporal properties, but they view the system as a whole instead of proving temporal properties in a modu-lar way. This means that it is hard to use the model checkers to prove temporal properties that concern calls between different modules.

As illustrated with the aircraft example, a problem with the division of formal verification into two distinct categories is that we have to make an ex-clusive choice between verifying modular programs and verifying temporal properties (for example reaction properties). An interesting aspect is, there-fore, the possibility of combining these two techniques into something that allows for proving temporal properties of procedural programs. One approach to do this could be to design a framework of rules that describes how the tem-poral properties could be shown using the style of deductive verification. From this framework, it could be possible to use existing tools for deductive verifi-cation to show temporal properties for the program. If this could be achieved, it might be possible that the modular properties of deductive verification allow us to prove temporal properties for modular programs and systems.

Another aspect of formal verification is that it is often both complex and time-consuming. When using deductive verifiers it is often required to man-ually add annotations in the code that describes the properties that should be proven. For temporal logic, an abstract model of the program has to be cre-ated. Also, this is often both complex and time-consuming. If it would be possible to use the tools for deductive verification to also show temporal prop-erties, it could mean that some of the work that is carried out when doing the annotations for data transformation properties could be reused to also prove temporal properties. This would, in turn, mean that less time and work would be required when both temporal and data transformation properties should be

(11)

CHAPTER 1. INTRODUCTION 3

verified for the same program. For example, if annotations have already been done to show data transformation properties, it could be relatively cheap to reuse the annotations to also show temporal properties.

While there exist frameworks and ideas for how to show temporal proper-ties for modular programs, the use of existing tools for deductive verification to prove temporal properties is not something that has been researched to a great extent. There are many tools available that can check if a program is cor-rect based on some specifications using deductive verification. For programs written in the C language, two commonly used tools for deductive verification are VCC and Frama-C.

In this report, a framework for how to prove some temporal properties us-ing tools for deductive verifications for programs written in C language will be described. Example programs will be created to demonstrate how the frame-work could be applied in practice on a real C code. The frameframe-work used will be an extension of an existing framework for proving temporal properties using Hoare-style reasoning. The existing framework has been developed by Alur and Chaudhuri [1], the reader is encouraged to read their paper and familiar-ize themselves with their framework since this report builds heavily on their work. Alur and Chaudhuri’s framework gives a way to prove temporal prop-erties in a style similar to deductive verification. However, their framework is only defined for a simplistic theoretic program language. They do not use the framework together with any existing deductive verifier to prove tempo-ral properties for real program code. A more detailed description of Alur and Chaudhuri’s framework is given in section 2.3.

1.1

Research Question

In their 2010 paper "Temporal reasoning for procedural programs" [1] Alur and Chaudhuri present a logical framework for proving temporal properties of a program using Hoare-style reasoning. Their framework concerns three classes of temporal properties: safety, response, and reactivity. The frame-work consists of four logical rules for showing these properties. See section 2.3.

This project will aim to answer the following question: How can Alur and Chaudhuri’s framework be applied to C programs using existing tools for de-ductive verification to prove temporal properties? The project will build upon the rules for temporal properties of procedural programs that are defined in their paper [1] and try find a way to apply the rules on real programs writ-ten in the C language in a way that allows for proving (or disproving) temporal

(12)

4 CHAPTER 1. INTRODUCTION

properties of the program. Ultimately, the purpose is to inspire future research into this temporal logic framework and what parts of it could be automated by showing practical examples of these rules in action.

Due to time constraints of this project, only two of the four rules of the framework will be explored, L-Safe and L-Resp. Despite this, considering that the rules work in a very similar way, the conclusions can mostly be extended to the last two rules as well.

(13)

Chapter 2

Background

This report aims to apply an existing framework with the rules for proving temporal properties as developed by Alur and Chaudhuri for verifying tem-poral properties of C programs using existing tools for deductive verification. Their rules are developed and proven to be correct by using an underlying theory called nested words [1]. This chapter begins with a short introduction to temporal logic followed by a description of nested words. Then a detailed description of the framework developed by Alur and Chaudhuri is presented followed by a brief summary of existing tools for formal verification. Lastly, a description of the properties of the C language that are relevant to this report presented.

2.1

Temporal Logic

Temporal logic is a branch of mathematical logic. It captures properties re-lating to time [2]. For example "I am always tired", or "I will be hungry until I eat something" are temporal properties. While there are many frame-works capturing temporal logic they commonly contain five unary operators (N, F, G, A, E) and two binary operators (U, R). Furthermore, the four logi-cal operators (¬, ∨, ∧, ⇒) are usually borrowed from propositional logic. As stated, the five unary operators specify properties relating to time. F φ, for example, is short for future and says that the formula φ has to hold eventu-ally. Gφ is short for globally and says that φ must always hold. The binary operators relate two different formulas. φ U ψ, for example, says that φ has to hold until ψ does. The examples above would then be written as G tired and hungry U eat. These operators are also sometimes written with symbolic notation. For example, F φ can be written as ♦φ and Gφ as φ [2]. See table

(14)

6 CHAPTER 2. BACKGROUND

Operator Symbol Description

N φ ◦φ φ holds in the next state

F φ ♦φ φ holds eventually

φ φ holds always

Aφ ∀φ φ holds along all possible paths

Eφ ∃φ there exists a path to a state where φ holds

φ U ψ φ U ψ φ holds until ψ holds

φ R ψ φ Rψ ψ holds until the first state φ holds

Table 2.1: Temporal logic operators

2.1 for each symbol and a description of each operator.

2.2

Nested words

The theory of nested words is not used in the method of this report but the framework this report builds on[1] is founded on the nested word concept. For example, the soundness proofs that are given in the report are given in a context of nested words. Therefore, a description of the concept is appropriate.

How temporal logic can be used to handle nested calls and returns is first introduced by Alur et al in 2004 [3]. Nested words are able to represent both a linear and hierarchical structure. They are useful in formal verification since they capture the procedural nature of languages like C. The linear property captures the linear execution flow of C programs, while the hierarchical struc-ture capstruc-tures the hierarchical nastruc-ture of function calls. As such, the nested word concept is a valuable theoretical tool in procedural contexts.

The concept of nested words is defined to be words over some alphabet along with call tags (<) and return tags (>). See table 2.3 for an example of a nested word. A nested word can thus be viewed both in regards to the current procedure and in regards to calls and returns to and from other procedures. A word is considered to be a matched word if it for each call tag, also contains a corresponding return tag. If a word has a call tag with no corresponding return tag, it is an unmatched word and that call tag is called a pending call tag. See examples in 2.3 and 2.2. The part of a word (subword) that is between a matched pair of call and return tags could be seen as a procedure and every procedure can contain infinitely many nested subprocedures.

Figure 2.1 shows a C program where a translation of some C code into a nested word is shown. The main function calls the multiply_with_two func-tion who in turn calls the general multiply funcfunc-tion. This could be translated

(15)

CHAPTER 2. BACKGROUND 7 1 i n t m u l t i p l y (i n t x , i n t y ) { 2 i n t z ; / / w4 3 z = x ∗ y ; / / w5 4 r e t u r n z ; / / > 5 } 6 7 i n t m u l t i p l y _ w i t h _ t w o (i n t x ) { 8 i n t z ; / / w3 9 z = m u l t i p l y ( x , 2 ) ; / / < w6 10 r e t u r n z ; / / > 11 } 12 13 i n t main ( ) { / / < 14 i n t x , z ; / / w1 15 x = 1 ; / / w2 16 z = m u l t i p l y _ w i t h _ t w o ( x ) ; / / < w7 17 p r i n t f ("X ∗ 4 = %d \ n ", z ) ; / / w8 18 r e t u r n 0 ; / / > 19 }

Figure 2.1: Example of a C program translated into a nested word.

into the nested word < w1w2 < w3 < w4w5 > w6 > w7w8, where w1,

w2, w7, and w8 is local statements in the main function, w3 and w6 is the

multiply_with_two function, and w4 and w5 is the general multiply

func-tion. Furthermore, the call tags correspond to function calls and the return tags correspond to function returns. The alphabet is the allowed statements of the C language, the comments in Figure 2.1 clarify what statement is mapped

to what wi. Note that, for example, the line z = multiply_with_two(x, 2);

is translated into < w7, both a call tag and a wi, because it includes both a function call and an assignment to the variable z. It should also be mentioned that the translation to the nested word also includes the call and return tags of the main function. Note that the example program is not a formal translation into nested words and is simply an example to clarify the concept.

Alur et al further develop the theory of nested words in 2008 [4] where they define temporal logic for nested words as well as proving that the satisfiability problem of nested word temporal logic is EXPTIME-complete.

To define rules for a nested word, three different types of paths through a nested word is defined in [1]: local, global and staircase paths. The local path is the path generated by first removing all matched subwords and if there are any (pending) call tags left, remove the call tag and everything that follows after the call tag. This means that the local path only corresponds to what

(16)

8 CHAPTER 2. BACKGROUND

Nested word w1w2 < w3 < w4 > w5 > w6

Local path w1w2w6

Global path w1w2w3w4w5w6

Staircase path w1w2w6

Table 2.2: Paths for matched nested word Nested word w1w2 < w3 < w4 > w5 > w6 < w7w8

Local path w1w2w6

Global path w1w2w3w4w5w6w7w8

Staircase path w1w2w6w7w8

Table 2.3: Paths for unmatched nested word

happens in the current procedure and ignores the assignments and statements that are done in some other procedure that is called, pending or not, from the current procedure. The global path is simply the path with all call and return tags removed. It, therefore, contains all assignments and statements that are done in the current procedure and in any subprocedure that is called from the current procedure. The staircase path is the path generated by first removing all matched subwords from the word and then removing all remaining call tags. The staircase path thus contains the current procedure together with subpro-cedures that are called but never returns (i.e. a call that never returns).

Table 2.3 gives the global, local and staircase paths for the unmatched word {w1, w2, <, w3, <, w4, >, w5, >, w6, <, w7, w8}, where widescribes a state-ment in the language, < is a call tag and > is a return tag. Table 2.2 gives the same paths for the matched word {w1, w2, <, w3, <, w4, >, w5, >, w6}. Note that, in a matched word, the staircase path will be the same as the local path.

2.3

A framework for temporal logic

The framework for proving temporal properties is defined by Alur and Chaud-huri in 2010 [1]. There they define four different rules; L-Safe, L-Resp, G-Resp, and S-React (see description below). The rules are defined for a sim-ple theoretic programming language which uses the basic components of most normal programming languages. The rules offer a way to prove temporal prop-erties for programs that consists of different procedures where the procedures can call each other. In the framework, they model the program as a nested word, which they use in formal proofs of soundness and relative complete-ness.

(17)

CHAPTER 2. BACKGROUND 9

The rules are based on what Alur and Chaudhuri call an inductive pair (I, Ψ) for each procedure p in the program. The element I is a local invariant. This states logical formulas that are true at all execution points of procedure. This could, for example, be that some variable x is is always even in the pro-cedure p. This would be written as I(p) ⇒ (x mod 2 = 0). You could then write that for the procedure ` x mod 2 = 0 holds. The element Ψ is the summary of the procedure and states logical formulas that are true when a procedure ends (i.e at the procedure return).

The first rule, L-Safe, deals with local safety properties. These properties states that some formula φ holds at all points within the current procedure. Intuitively, these are properties that you want to always be true in the program or procedure, no matter what happens in the code. Given an inductive pair defined as above for some procedure p in the program P, the rule proves that if an inductive pair (I, Ψ) can be found where I(p) ⇒ φ, then φ is always true within every execution point of the procedure p. See the formal definition in figure 2.2.

Input: A procedure p in the program P and a formula φ.

Rule: For a procedure p in the program P, find an inductive pair (I, Ψ) such

that the following holds: 1. ` I(p) ⇒ φ

P, p `



Figure 2.2: Premises for the L-Safe rule

The second rule, L-Resp, deals with local response properties. Intuitively,

this rule says that if the formula φ1is true at some execution point there must

exist a future execution point in this procedure where φ2 is true. This could

capture behavior such that if at some point in a procedure p a file is opened, there must exist a future point in p where the same file is closed. As before, this rule requires an inductive pair (I, Ψ). It also requires a ranking function, which is a function from a set of execution states to some set of values upon which there exists an ordering [1]. This function has to decrease in value

along the execution path from the state where φ1holds until the state where φ2

holds. Intuitively, the ranking function can be thought of as a way to measure

how far away from the goal (φ2) you are. If this ranking function is defined

so that the value always decreases along the local path, we must eventually reach the goal. This can also be seen as a generalization of a loop variant.

(18)

10 CHAPTER 2. BACKGROUND

A loop variant is a common concept in formal verification and is some value or proposition that changes every iteration of a loop. Loop variants are often used to prove termination of a loop [5]. See figure 2.3 for a formal description of the L-Resp rule and its premises. In the figure, some of the premises are given as Hoare triples. A Hoare triple is written as {P }C{Q} and indicates that if a statement P holds before running the program C, then Q will hold if the program terminates. In the premises, in figure 2.3 the "program" C is a single execution point in the procedure p.

Input: A procedure p in the program P and formulas φ1, φ2.

Rule: Find an inductive pair (I, Ψ) and a ranking function δ on execution

states, a formula k, for each procedure q called from p a formula kq, and

recur-sively for each procedure r called from q a formula krsuch that the following

holds:

1. ` φ1 ⇒ φ2∨ k

2. For each local execution point and summary execution point e in p:

` {k ∧ (δ = d)} e {φ2∨ (k ∧ (δ < d))}

3. For each local execution point and summary execution point e in q: ` {kq∧ (δ = d)} e {(pc =⊥q) ∨ (kq∧ (δ < d))}

4. For each calling execution point e in p to q: ` {k ∧ (δ = d)} e {kq∧ (δ < d)}

5. For each calling execution point e in q to r: ` {kq∧ (δ = d)} e {kr∧ (δ < d)}

P, p `



l(φ1 → ♦lφ2)

Figure 2.3: Premises for the L-Resp rule

The third rule, G-Resp, is similar to the L-Resp rule. The difference is that

the points at which φ1and φ2hold do not have to be in the same procedure and

the property has to hold in the entire program, not just in one procedure. As before this states that, if φ1is true at some execution point, there exists a future execution point where φ2 is true. The difference is that it applies to the global

path, not the local path. Both the point where φ1 first holds and the point at

which φ2 holds could be in (different) subprocedures. To prove this rule, the

program is modified by adding the line if(φ2)#p,φ2 = true; between all

lines of the program, where #p,φ2 is a local variable to the procedure p. They

(19)

CHAPTER 2. BACKGROUND 11

this equal to the local #p,φ2. This modified program P

φ2

effectively tracks

if φ2 is satisfied at any point of the program. This modification is used in

conjunction with an inductive pair (I, Ψ) and a ranking function, similarly to the L-Resp rule. For a formal definition of the premises for this rule, see 2.4.

Input: A program P and formulas φ1, φ2.

Rule: For the program Pφ2

find an inductive pair (I, Ψ), a ranking function δ on execution states, and for each called procedure q a formula kq, such that the following holds:

1. For each execution point in q: ` φ1 ⇒ (φ2∨ kq)

2. For each local execution point e in q: ` {kq∧ (δ = d)} e {φ2∨ (kq∧ (δ < d))} 3. For each calling execution point e in q to r:

` {kq∧ (δ = d)} e {φ2∨ (kr∧ (δ < d))} 4. For each summary execution point e in q:

` {kq∧ (δ = d)} e {¬#φ2 ⇒ φ2∨ (kq∧ (δ < d))}

P, p `



g(φ1 → ♦gφ2)

Figure 2.4: Premises for the G-Resp rule

Lastly, the fourth rule, S-React, deals with staircase reactivity. This is the most advanced rule and can be used to show properties along the staircase path.

This rule states that if a formula φ1 holds infinitely often then φ2 also holds

infinitely often. A third assertion θ is also introduced. Intuitively, this rule

captures when a program will always react to some event/state φ1by eventually

reaching an event/state φ2. See figure 2.5 for a formal definition of this rule’s premises.

In their paper, Alur and Chaudhuri show that these rules are sound and rel-atively complete. Soundness means that if the framework can be used to show that some property holds then this property is indeed true for the program. In other words, soundness means it is not possible in the system to prove any false properties. Completeness means that if a property for a program holds, the rules can always be used to prove that it is true. The restriction to rela-tive completeness means that it is only complete under the assumption that we know if a program would terminate or not. The idea is that, if it would exist some oracle that can tell us if the program terminates or not, then the rules are

(20)

12 CHAPTER 2. BACKGROUND

Input: A procedure p in the program P and formulas φ1, φ2, θ.

Rule: Find an inductive pair (I, Ψ), a formula k, a ranking function δ on

execution states, and for each called procedure q a formula kq, such that the

following holds:

1. For all execution points in q: ` φ1 ⇒ (φ2∨ kq)

2. For each local execution point in q:

` {k ∧ (δ = d) ∧ θ} e {φ2∨ (k ∧ (δ < d))}

` {k ∧ (δ = d)} e {φ2∨ (k ∧ (δ ≤ d))}

` {kq∧ (δ = d) ∧ θ} e {(pc =⊥q) ∨ φ2∨ (kq∧ (δ < d))}

` {kq∧ (δ = d)} e {(pc =⊥q) ∨ φ2∨ (δ ≤ d)}

3. For each calling execution point in q to r:

` {k ∧ (δ = d) ∧ θ} e {kr∧ (δ < d)}

` {k ∧ (δ = d)} e {kr∧ (δ ≤ d)}

` {kq∧ (δ = d) ∧ θ} e {kr∧ (δ < d)}

` {kq∧ (δ = d)} e {kr∧ (δ ≤ d)}

4. For each summary execution point in q:

` {k ∧ (δ = d) ∧ θ} e {φ2∨ (k ∧ (δ < d)}

` {k ∧ (δ = d)} e {φ2∨ (k ∧ (δ ≤ d)}

` {kq∧ (δ = d) ∧ θ} e {(pc =⊥r) ∨ φ2∨ (kr∧ (δ < d))}

` {kq∧ (δ = d)} e {(pc =⊥r) ∨ φ2∨ (kr∧ (δ ≤ d))}

P, p `



s((φ1∧



s♦sθ) → ♦sφ2) Figure 2.5: Premises for the S-React rule

complete. However, in reality it is not possible to always determine if a pro-gram will terminate or not. This is a well known undecidable problem, usually referred to as the halting problem. Note that this is not a restriction specific to this framework, indeed this is something most logical systems are subject to.

2.4

Tools for formal verification

In this section, a brief description of three different categories of tools used to verify correctness properties for programs is given. For each category, exam-ples of existing tools are presented.

(21)

CHAPTER 2. BACKGROUND 13

2.4.1

Deductive verifiers

A deductive verifier is a program that takes as input a program together with some specification of how that program is supposed to behave and verifies whether the program satisfies the specification. Usually, a deductive verifier only works for a specific programming language.

In deductive verifiers, the specifications are typically given as annotations in the code. The annotations are often given as comments that follow some syntax specified for the deductive verifier. These annotations could, for exam-ple, be an assertion or a loop invariant. The deductive verifier then checks if the program is correct relative to the specifications given in the annota-tions. For each function, specifications can typically be given as pre- and post-conditions and writes-post-conditions. Pre- and post-post-conditions tell what will be true when exiting the function given that the precondition was true when enter-ing it. The write-conditions describes what can be edited by the function (i.e. write-access). Another part is type invariants that specify some conditions that must always be fulfilled for some type of variables [6]. Internally deductive verifiers often work by converting the problem of program correctness into a satisfiability modulo theories (SMT) problem such that if the program does not follow the specification then the SMT formula is not satisfiable. The cre-ated formulas are then passed on to an SMT solver, which is a tool that tries to show whether an SMT formula is satisfiable or not. See section 2.4.3.

One example of a deductive verifier for programs in the C language is VCC. It was developed by Microsoft to formally verify their hypervisor program. This is a complex program that sits on top of operating systems, allowing multiple operating systems on the same machine running concurrently. VCC is unfortunately not actively developed anymore.

Another deductive verifier that is similar to VCC is Frama-C. It is also based on creating annotations for the code. Frama-C allows ghost operations. These are operations that are not part of the actual program but can be used to for example assert that objects/variables are in some specific state or to track some property. With ghost operations, you can introduce new variables to for example track some value throughout the program. Frama-C has a plugin called Aoraï which is related to this project. It can be used to show that some LTL formulas hold for a given C program [7]. It can currently only verify the safety properties of LTL formulas. Aoraï works by modeling the program as a Büchi automaton and verifying that the program behaves according to some given automaton. Internally, it encodes the specified automation in Frama-C annotations.

(22)

14 CHAPTER 2. BACKGROUND 1 / ∗@ 2 r e q u i r e s x >= 0 && y >= 0 ; 3 e n s u r e s \ r e s u l t == \ o l d ( x ) ∗ \ o l d ( y ) ; 4 ∗ / 5 i n t m u l t i p l y _ p o s (i n t x , i n t y ) { 6 / / @ghost i n t g h o s t _ y = y ; 7 i n t r e s u l t = x ; 8 9 / / @loop i n v a r i a n t r e s u l t == x ∗ ( g h o s t _ y − y + 1 ) ; 10 w h i l e( y > 1 ) { 11 r e s u l t += x ; 12 y−−; 13 } 14 / / @ a s s e r t r e s u l t == x ∗ g h o s t _ y ; 15 r e t u r n r e s u l t ; 16 }

Figure 2.6: Annotations example in Frama-C

Figure 2.6 shows an example of how a program that has been annotated in Frama-C can look like. In the example, the multiply_pos function is supposed to calculate the product of multiplication of two positive integers. The input integers are the variables x and y and the result is returned in the variable re-sult. Using annotations, the initial value of y is saved in the ghost variable ghost_y (since its value is changed in the function). The precondition also asserts that x ≥ 0 ∧ y ≥ 0 to make sure the input is correct. After the result has been calculated, the ghost variable ghost_y can be used in the assertion and loop invariant to make sure that the calculations are correct. If Frama-c would not be able to prove that both the assertions are always true, the tool would show an assertion error. For this example, Frama-C will not be able to prove that the annotations hold since for example if y = 0 the post condition will not hold as well as not holding in the case of overflow due to large enough values of x and y.

2.4.2

Model Checkers

Model checkers are tools used to verify temporal properties for some state model. The state model typically consists of a set of states and a set of formu-las which are either true or false in each state. The model also describes which states transitions are possible together with what triggers a transition between the states. Based on this model, the model checker tries to disprove any

(23)

tem-CHAPTER 2. BACKGROUND 15

poral properties by trying to find a sequence of transitions within the model that implies that the temporal property is false. If an exhaustive search is car-ried out and no counterexample is found, it proves that the property is true. A consequence of temporal logic is that, if a model checker finds a counter example of a temporal property, it implies that the negation of that temporal property is true [8].

To prove temporal properties for C-programs using model checkers, it is first required to create a state model of the program. SAT-ABS [9] is a tool for automated generation of such a model. SAT-ABS takes a program written in C and creates an abstract model of the program and then uses a model checker to prove or disprove some property. A problem with model checkers is that they are bad at handling programs that consist of different procedures where the procedures call each other, perhaps even in a recursive manner [10]. This means that a model checker is usually good at showing global properties or that some property holds within a procedure that doesn’t call other procedures but struggles to show local properties of procedures that calls other procedures. The case where a property holds within an entire procedure but is temporarily violated in a subprocedure is something model checkers often cannot verify.

2.4.3

SMT solvers

SMT (Satisfiability modulo theories) is the decision problem of satisfiability for formulas expressed in first-order logic [11]. This is an extension of propo-sitional logic which introduces predicates and quantifiers such as ∀ (for all) and ∃ (exists). An SMT solver aims to check the satisfiability of such formu-las. The formulas can be derived from different logical domains, including for example Booleans (SAT), equality/inequality, arrays, and quantifiers. Since formulas can be of Boolean nature, the SMT solvers can be seen as an exten-sion of a SAT solver that deals with satisfiability only for Boolean formulas [12].

One example of an SMT solver is Z3. Z3 can solve formulas from the domains of Booleans (SAT), array theory, tuple theory, linear arithmetic, bit-vectors, and formulas containing quantifiers [11]. Z3 and other SMT solvers are often used as backends for deductive verifiers. Generally, a deductive ver-ifier will take the program along with the annotations and translate this into a large SMT formula which is then passed on to an SMT solver to check for satisfiability. This formula is constructed such that if it is satisfiable only if the program follows the specification. For example, Frama-C uses SMT solvers as back end. Normally, Frama-C uses an SMT-solver called Alt-Ergo but it

(24)

16 CHAPTER 2. BACKGROUND

can also be configured to use other SMT solvers, one of these being Z3. One could bypass deductive verifiers and use SMT solvers such as Z3 directly if the properties you want to show are easily modeled as an SMT formula [13].

2.5

The C language

The C language is one of the most common and influential programming lan-guages of all time [14]. It sees heavy use in for example embedded software, operating systems, and performance critical applications. The most recent specification of the language is given by ISO/IEC 9899:1999 [15], colloqui-ally known as C99. It is also a low level language where, for example, the pro-grammer has to manually handle memory management. This can sometimes be quite error prone. Thus C is of particular interest to formal verification.

As described in the aforementioned section on nested words 2.2, nested-words form a context-free language. This could be a problem since the C language does not form a context-free grammar. Ignoring the preprocessor, the keyword typedef introduces context-sensitivity. Take for example the following code:

int foo; typedef int foo; foo x;

The token foo can either be a type or an identifier depending on the con-text. This is only noteworthy on a theoretical level since the underlying theory of the aforementioned framework is founded on the nested word structure but this does not pose a problem this practical application of the framework.

(25)

Chapter 3

Method

In this project, we show how to define a logical framework for proving tempo-ral properties of programs written in the C language using deductive verifiers. To do this we build upon an existing framework for proving temporal proper-ties of programs written in a simpler language as defined in [1]. In this report, we will attempt to show how the rules defined in [1] can be used to prove tem-poral properties proven for C programs using the deductive verifier Frama-C. This will be done by describing procedures for translating the premises for each rule into annotations for Frama-C and letting Frama-C prove or disprove the annotations. To prove (or disprove) the annotations in the program, the Frama-C plugin WP [16] was used. WP proves that the annotations hold by creating proof obligations that are translated into an SMT formula. The math-ematical formula is in turn handed to the SMT solver Alt-Ergo that checks if the formula is satisfiable or not.

The reason for using the framework of rules as defined by [1] is to explore the practical feasibility of the framework. Their framework contains rules that are defined in a style similar to deductive verification, using Hoare triplets. Therefore, we believe it is feasible to translate their rules into annotations for an existing deductive verifier. Since they also give soundness and relative com-pleteness proofs, the similarities between their framework and deductive veri-fication could mean that the veriveri-fication of C programs using Frama-C is also sound and relatively complete. As such, one could bridge the gap between the for now separated areas of deductive verification and model checking.

To show how the rules can be applied on real programs, example programs in the C language will be constructed. Then some temporal properties for the programs will be proved to hold using the described translation of the premises into annotations. The rules that will be proven are the same as are shown in

(26)

18 CHAPTER 3. METHOD

the framework in [1]. By showing how these four rules could be verified using this process together with a deductive verifier, the aim is to show the potential of using this method to prove temporal properties of programs written in the C language. With this, we also hope to inspire future research into automating parts of the process.

(27)

Chapter 4

Results

In the following sections, results for each rule is described. As noted, due to time constraints only L-Safe and L-Resp are included. Results include de-scriptions of how to translate premises of each rule into annotations as well as an example program where this translation from premises to annotations are used to prove a temporal property.

4.1

Rule L-Safe

For the rule L-Safe converting to annotations is trivial, since the only premise is I(p) → φ (see figure 2.2). This means you only need to show that φ is a local invariant of some procedure. L-Safe for a procedure p and a formula φ can be shown in full using deductive verifiers by asserting φ at every execution point in the procedure p and proving relevant pre- and post-conditions of all subproceducers of p. For loops, assertion before the first and after the last statement of the loop body can be replaced with a loop invariant that implies φ. By doing it is obvious that the prerequisite of L-Safe is met.

In figure 4.1, a simple example program P is presented. It is supposed to model a queue ticketing system. Queuing numbers are dealt out in increas-ing order but when it reaches 100 it should wrap around and start over from 0 again. Formally, let φ = ticket < 100. For the example program,

con-sider the following temporal property: P, main `



lφ. This means no one

could ever be assigned a ticket number with more than two digits. To show this property we must have that I(main) → φ, as per the precondition of L-Safe. This means that φ must be a local invariant of main. Only the invariant of main and the summary of the subprocedure foo are the relevant parts of the inductive pair for this program. Given that the while loop only contains one

(28)

20 CHAPTER 4. RESULTS 1 u n s i g n e d i n t t i c k e t ; 2 3 / ∗@ 4 r e q u i r e s t i c k e t ! = 4 2 9 4 9 6 7 2 9 5 ; 5 e n s u r e s ( \ o l d ( t i c k e t ) == 99 && t i c k e t == 0 ) | | 6 ( \ o l d ( t i c k e t ) ! = 99 && t i c k e t == \ o l d ( t i c k e t ) + 1 ) ; 7 ∗ / 8 v o i d f o o (v o i d) { 9 ++ t i c k e t ; 10 i f ( t i c k e t == 1 0 0 ) { 11 t i c k e t = 0 ; 12 } 13 } 14 15 i n t main (v o i d) { 16 t i c k e t = 0 ; 17 / /@ a s s e r t t i c k e t < 1 0 0 ; 18 / /@ l o o p i n v a r i a n t t i c k e t < 1 0 0 ; 19 w h i l e ( 1 ) { 20 f o o ( ) ; 21 } 22 }

Figure 4.1: L-Safe example program

statement the loop invariant covers all execution points of the loop. Therefore at all execution points of main φ is asserted. The summary of foo corre-sponds to its post-condition. Frama-C successfully proves all assertions of

this program meaning the property P, main `



lφ has been proved to hold

for the program.

Note that φ can be violated inside the execution context of foo if ticket = 99 at the start of foo. Here we see the power of this framework, being able to express safety properties in a local context not just globally. The formula φ is violated inside a subprocedure but never in the local context of main.

The precondition of foo (ticket 6= 4294967295) is required to show the postcondition. This is to avoid the case of overflow in the increment op-eration. This is fine since this pre-condition will be true for all calls of foo from main.

(29)

CHAPTER 4. RESULTS 21

4.2

Rule L-Resp

As we can see from the premises defined in figure 2.3, L-Resp is a lot more

complex than L-Safe. The first premise ` φ1 ⇒ φ2∨k can, as with L-Safe,

eas-ily be verified by simply asserting it at every execution point. Premise two can be verified in a similar way. Since it is defined as a Hoare triple, a ghost

vari-able kpre can be added which captures whether the precondition holds. After

the execution point an assertion can then be made saying that the precondition

must imply the postcondition, e.g kpre ⇒ φ2∨(k ∧(δ < d)). For the summary

execution points, the ensures annotation can be used to get information from the function that has been called. Premise three is the same as premise two, just in the context of the subprocedures of p, and is therefore handled in the same way. Additionally, premise four can be annotated by using the requires annotation for the function that is being called. Lastly, premise five is identical to premise four just in the context of subprocedures and can also be handled using the requires annotation. As such, while the L-Resp rule is considerably more complex than l-safe, it can certainly be verified fully using a deductive verifier. The difficulty lies in finding a suitable ranking function and a formula k.

In the example program, P below, a simple queue ticket printer is mod-eled. The L-safe property we want to show is that enabling the printer means

a ticket will be printed. Formally, let φ1 = printer_enabled and φ2 =

ticket_printed. We want to show P,main `



l(φ1 → ♦lφ2). The

frame-work includes a reference to a program counter. This is roughly equal to the line number or to label each statement with a unique label. To model a pro-gram counter, a ghost variable label is introduced which can be referenced in subsequent formulas. Furthermore, a second ghost variable prev_i is used to be able to reference the value of i at the previous execution point. This is needed in the ranking function. We define the ranking function’s do-main to be pairs (pc, i), where pc is the program counter or label and i the current value of the variable i in the program below at some execution point. The ordering of these elements are as follows: (pc1, i1) < (pc2, i2) iff i1 < i2 ∨ (i1 = i2 ∧ pc1 < pc2). Note that for points previous to where i is defined the value of i1and i2 is considered equal. Lastly, for the L-resp rule a formula k is needed, which we define to be label ∈ [4, 8] ∧ ¬ticket_printed. Note that the two called functions from main do not affect anything regarding

φ1and φ2and have thus been excluded from this proof for brevity and from the

source code below. The full program together with the annotations in the non-simplified form can be found in appendix A. Here we also include annotations

(30)

22 CHAPTER 4. RESULTS

for the functions that are called from main.

With the assertions made as described above with the definitions of the

ranking function, k, φ1, and φ2, the Frama-C annotated program is as below.

This follows the annotation construction as defined above. Note that the an-notations have been simplified to increase readability and to better fit in this document. These are not valid Frama-C annotations since "k" for example is not defined. The formulas phi1, phi2, k should be replaced with formulas as they are defined above. They, of course, were when running Frama-C on this program. Frama-C successfully shows all of the 21 assertions of the program.

The L-Resp property P,main `



l(φ1 → ♦lφ2) is therefore considered

proven. 1 i n t main ( ) { 2 i n t p r i n t e r _ e n a b l e d = 0 , t i c k e t _ p r i n t e d = 0 ; 3 / / @ghost i n t l a b e l = 1 ; 4 w h i l e( 1 ) { 5 / / @ghost i n t p r e _ k = k ; 6 p r i n t e r _ e n a b l e d = t i c k e t _ p r i n t e d = 0 ; 7 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && l a b e l < 3 ) ) ; 8 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 9 / / @ghost l a b e l = 3 ; 10 11 / / @ghost p r e _ k = k ; 12 i n t u s e r _ i n p u t = g e t _ i n p u t ( ) ; 13 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && l a b e l < 4 ) ) ; 14 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 15 / / @ghost l a b e l = 4 ; 16 17 i f ( u s e r _ i n p u t ) { 18 / / @ghost p r e _ k = k ; 19 p r i n t e r _ e n a b l e d = 1 ; 20 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && l a b e l < 5 ) ) ; 21 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 22 / / @ghost l a b e l = 5 ; 23 24 / / @ghost p r e _ k = k ; 25 i n t i = 0 ; 26 / / @ghost i n t p r e v _ i = −1; 27 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && ( l a b e l < 6 | | p r e v _ i < i ) ) ) ; 28 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 29 / / @ghost l a b e l = 6 ; 30 31 / ∗ @loop i n v a r i a n t p h i 1 ==> ( p h i 2 | | k ) ; 32 @loop i n v a r i a n t p r e v _ i < i ; ∗ / 33 w h i l e( i < NUM_LINES ) {

(31)

CHAPTER 4. RESULTS 23 34 / / @ghost p r e _ k = k ; 35 p r i n t _ l i n e ( i ) ; 36 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && ( l a b e l < 7 | | p r e v _ i < i ) ) ) ; 37 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 38 / / @ghost l a b e l = 7 ; 39 40 / / @ghost p r e v _ i = i ; 41 / / @ghost p r e _ k = k ; 42 i = i + 1 ; 43 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && ( l a b e l < 8 | | p r e v _ i < i ) ) ) ; 44 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 45 / / @ghost l a b e l = 8 ; 46 } 47 48 / / @ghost p r e _ k = k ; 49 t i c k e t _ p r i n t e d = 1 ; 50 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && l a b e l < 9 ) ) ; 51 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 52 / / @ghost l a b e l = 9 ; 53 54 } 55 / / @ghost p r e _ k = k ; 56 p r i n t e r _ e n a b l e d = 0 ; 57 / / @ a s s e r t p r e _ k ==> ( p h i 2 | | ( k && l a b e l < 1 0 ) ) ; 58 / / @ a s s e r t p h i 1 ==> ( p h i 2 | | k ) ; 59 / / @ghost l a b e l = 0 ; 60 } 61 }

(32)

Chapter 5

Discussion

We believe that the results show promise in the realm of using tools for deduc-tive verification to show temporal properties. We managed to succeed in using Frama-C to show temporal properties of simple programs. Clear procedures are described in section 4 on how to convert the premises of L-Safe and L-Resp into code annotations in a deductive verifier such as Frama-C. Unfortunately, due to time constraints the rules G-Resp and S-React were not researched. The premises G-Resp and S-React function in a very similar way with local invari-ants, ranking functions, and Hoare triples over execution points. We see no reason as to why these two rules could not be encoded in a deductive verifier as well. From this, we draw the conclusion that it is possible to prove temporal properties with the framework described in 2.3 using deductive verifiers.

As for the practical side of applying the framework using deductive veri-fiers, a clear drawback of this technique is the amount of work needed from the programmer’s side. The number of annotations needed for showing the premises for example quickly snowballs. As seen in the example for the L-resp rule, see 4.2, several assertions, and ghost variable operations are needed for each line of the program. The example program presented in section 4.2 is a modest 13 lines without code annotations. To prove the simple L-Resp property an additional 50 lines of Frama-C annotations is required. This is an increase of program length by more than a factor of four. Also, consider that the temporal property verified in the examples is quite trivial.

However, while this is a large increase of program length, the rules for cre-ating the annotations are to some extent strictly mechanical. For example, to show the local invariants needed (see for example premise one of L-Resp in figure 2.3) an assertion has to be made at every single execution point. This could easily be automated and done via a single local-invariant-annotation. As

(33)

CHAPTER 5. DISCUSSION 25

for the Hoare triplet at each execution point, labeling each row by assigning a ghost variable and asserting the first premise in the L-Resp rule are mechan-ical. Also this could easily be automated since the assertion is the same for all execution points. Lastly, the program counter could also be simplified with tooling support, something simulated in this report by a ghost variable assign-ment on every line.

Although the amount of work could certainly be decreased with tooling support, automating the entire process is probably not possible. The program-mer has to come up with a suitable ranking function and formula k for the L-Resp rule. For the simple example in section 4.2, this was not complicated but for a more convoluted example, one could imagine this being very diffi-cult. Automating this process would also not be easy. Ranking functions are a generalization of a loop variant which is something that is not automated today by tools like Frama-C. There has been some research into automatically finding ranking functions. Podelski and Rybalchenko provide a method for synthesizing ranking functions in programs with unnested loops [17] for use in proving termination. They show that their method is complete. This shows some promise in automatically finding suitable ranking functions but so far handling the general case has not been done and whether it is possible is un-clear. Coming up with a suitable formula k for the L-Resp rule is also not trivial and could be very difficult to automate.

In this project, Frama-C was chosen as the deductive verifier for proving temporal properties. It was chosen because it has good support for programs in the C language and because it is a tool with active use and development today. We believe it will continue to be used by companies and researchers. Another option would have been to use VCC which possibly could have simplified the results since VCC offers support for local invariants. However, using a more active tool was valued higher but that sacrificed some simplicity in the results. Also, adding support for local invariants to Frama-C would not be difficult.

(34)

Chapter 6

Conclusion

The results of this report show that it is certainly possible to show tempo-ral properties of C programs using deductive verifiers together with Alur and Chaudhuri’s framework. In practice, on the other hand, the system is not very feasible to use manually since it requires extensive work annotating the code and coming up with suitable formulas and ranking functions. However, much of the work during the annotation process could be automated. For example, some premises require the same assertion on every line. With proper tooling support, this and other premises could be automated and perhaps replaced with a single annotation. Full automation is probably not possible since automati-cally generating a suitable ranking function is most likely not possible.

In conclusion, while the framework can certainly be used to prove tempo-ral properties using deductive verifiers, it would require tooling support to be practical.

(35)

Bibliography

[1] Rajeev Alur and Swarat Chaudhuri. “Temporal reasoning for procedural

programs”. In: International Workshop on Verification, Model Check-ing, and Abstract Interpretation. Springer. 2010, pp. 45–60.

[2] Michael Fisher. An introduction to practical formal methods using

tem-poral logic. Vol. 82. Wiley Online Library, 2011.

[3] Rajeev Alur, Etessami Kousha, and P. Madhusudan. “A Temporal Logic

of Nested Calls and Returns”. In: TACAS 2004: Tools and Algorithms for the Construction and Analysis of Systems. Springer. 2004, pp. 467– 481.

[4] Alur et al. “First-order and temporal logics for nested words”. In:

Log-ical Methods in Computer Science, vol 4. University of Pennsylvania. 2008, pp. 1–44.

[5] Dennis Dams, Rob Gerth, and Orna Grumberg. “A heuristic for the

au-tomatic generation of ranking functions”. In: Workshop on advances in verification. 2000, pp. 1–8.

[6] Ernie Cohen et al. “VCC: A Practical System for Verifying

Concur-rent C”. In: TPHOLs 2009: Theorem Proving in Higher Order Logics. Springer. 2009, pp. 23–42.

[7] Nicolas Stouls and Virgile Prevosto. “Aorai Plugin Tutorial”. In:

Rap-port technique, Frama-C (2013), p. 57.

[8] Edmund M. Clarke et al. Handbook of Model Checking. Springer, 2018.

Chap. 1.2.

[9] “SATABS: SAT-Based Predicate Abstraction for ANSI-C”. In: 2005,

pp. 570–574.

(36)

28 BIBLIOGRAPHY

[10] Siavash Soleimanifard and Dilian Gurov. “Algorithmic verification of

procedural programs in the presence of code variability”. In: TScience of Computer Programming, volume 127. KTH Royal Institute of Tech-nology. 2015, pp. 76–102.

[11] Leonardo De Moura and Nikolaj Bjørner. “Z3: An efficient SMT solver”.

In: International conference on Tools and Algorithms for the Construc-tion and Analysis of Systems. Springer. 2008, pp. 337–340.

[12] Leonardo de Moura, Bruno Dutertre, and Natarajan Shankar. “A

Tuto-rial on Satisfiability Modulo Theories”. In: CAV 2007: Computer Aided Verification. Springer. 2007, pp. 20–36.

[13] Leonardo de Moura and Nikolaj Bjørner. “Z3: An Efficient SMT Solver”.

In: TACAS 2008: Tools and Algorithms for the Construction and Anal-ysis of Systems. Springer. 2008, pp. 337–340.

[14] TIOBE Group et al. TIOBE Index for May 2019. 2019. url: www .

tiobe.com/tiobe-index.

[15] ISO/IEC JTC 1. “ISO/IEC 9899:201x - Commitee draft”. In: 2011.

[16] Andreas Podelski and Andrey Rybalchenko. WP Plug-in Manual -

Frama-C 18.0 (Argon). Frama-CEA LIST, Software Safety Laboratory. 2010.

[17] Andreas Podelski and Andrey Rybalchenko. “A complete method for

the synthesis of linear ranking functions”. In: International Workshop on Verification, Model Checking, and Abstract Interpretation. Springer. 2004, pp. 239–251.

(37)

Appendix A

Full L-Resp example program

1 # i n c l u d e < s t d i o . h > 2 # d e f i n e NUM_LINES 5 3 4 / / @ghost i n t l a b e l ; 5 6 i n t g e t _ i n p u t ( ) ; 7 v o i d p r i n t _ l i n e (i n t l i n e _ n r ) ; 8 9 i n t main ( ) { 10 i n t p r i n t e r _ e n a b l e d = 0 , t i c k e t _ p r i n t e d = 0 ; 11 / / @ghost l a b e l = 1 ; 12 13 / / @loop i n v a r i a n t l a b e l <= 1 ; 14 w h i l e( 1 ) {

15 / / @ghost i n t p r e _ k = l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ; 16 p r i n t e r _ e n a b l e d = t i c k e t _ p r i n t e d = 0 ; 17 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && l a b e l < 2 ) ) ; 18 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 19 / / @ghost l a b e l = 2 ; 20

21 / / @ghost p r e _ k = ( l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 22 i n t u s e r _ i n p u t = g e t _ i n p u t ( ) ; 23 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && l a b e l < 4 ) ) ; 24 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 25 / / @ghost l a b e l = 4 ; 29

(38)

30 APPENDIX A. FULL L-RESP EXAMPLE PROGRAM

26

27 i f ( u s e r _ i n p u t ) {

28 / / @ghost p r e _ k = ( l a b e l >= 6 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 29 p r i n t e r _ e n a b l e d = 1 ; 30 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && l a b e l < 5 ) ) ; 31 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 32 / / @ghost l a b e l = 5 ; 33

34 / / @ghost p r e _ k = ( l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 35 i n t i = 0 ; 36 / / @ghost i n t p r e v _ i = −1; 37 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && ( l a b e l < 6 | | p r e v _ i < i ) ) ) ; 38 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 39 / / @ghost l a b e l = 6 ; 40 41 / ∗ @loop i n v a r i a n t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 42 @loop i n v a r i a n t p r e v _ i < i ; ∗ / 43 w h i l e( i < NUM_LINES ) {

44 / / @ghost p r e _ k = ( l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 45 i = i + 1 ; 46 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && ( l a b e l < 7 | | p r e v _ i < i ) ) ) ; 47 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 48 / / @ghost l a b e l = 7 ; 49

50 / / @ghost p r e _ k = ( l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 51 p r i n t _ l i n e ( i ) ; 52 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && ( l a b e l < 8 | | p r e v _ i < i ) ) ) ; 53 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 54 / / @ghost l a b e l = 9 ; 55 } 56

(39)

APPENDIX A. FULL L-RESP EXAMPLE PROGRAM 31

57 / / @ghost p r e _ k = ( l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 58 t i c k e t _ p r i n t e d = 1 ; 59 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && l a b e l < 1 0 ) ) ; 60 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 61 / / @ghost l a b e l = 1 0 ; 62 63 }

64 / / @ghost p r e _ k = ( l a b e l >= 4 && l a b e l <= 10 && !

t i c k e t _ p r i n t e d ) ; 65 p r i n t e r _ e n a b l e d = 0 ; 66 / / @ a s s e r t p r e _ k ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d && l a b e l < 1 0 ) ) ; 67 / / @ a s s e r t p r i n t e r _ e n a b l e d ==> ( t i c k e t _ p r i n t e d | | ( l a b e l >= 4 && l a b e l <= 10 && ! t i c k e t _ p r i n t e d ) ) ; 68 / / @ghost l a b e l = 0 ; 69 } 70 } 71 72 / ∗ @ r e q u i r e s ( l a b e l >= 4 && l a b e l <= 1 0 ) ==> ( l a b e l < 8 ) ; 73 @ e n s u r e s l a b e l == 9 ; 74 ∗ / 75 v o i d p r i n t _ l i n e (i n t l i n e _ n r ) { 76 / / S h o u l d c o n t a i n p r i n t f u n c t i o n a l i t y 77 / / @ghost l a b e l = 9 ; 78 } 79 80 81 / ∗ @ r e q u i r e s ( l a b e l >= 4 && l a b e l <= 1 0 ) ==> ( l a b e l < 3 ) ; 82 e n s u r e s l a b e l == 3 ; 83 ∗ / 84 i n t g e t _ i n p u t ( ) { 85 / / @ghost l a b e l = 3 ; 86 r e t u r n 1 ; 87 }

(40)
(41)
(42)

www.kth.se

References

Related documents

The aim of this master-thesis is to analyse perspectives on the vulnerability of the Swedish electricity distribution system related to climate and weather related risks, such

As Protégé does not provide a way to integrate it with a triple store, there was a need to create a tool that could be used to read in information in suit- able format, define

Pia Karlsson: Malignant melanoma in children and adolescents, and aspects of naevus phenotype in melanoma risk assessment. Jakob Paues: Brain stem involvement in immune and

Contrast sensitivity and transient VEP latency with colour and luminance patterns in subjects with normal binocular functions compared to stereoblind subjects with

Concrete cubes made with local recycled bricks are cast and tested for overall weight of concrete, moisture content, dynamic modulus of elasticity and compressive strength

We might not get into an exact explanation between tweeting activity and affected area, This figure suggests that affected places like New York and New Jersey are more

The aim of this work is to investigate the use of micro-CT scanning of human temporal bone specimens, to estimate the surface area to volume ratio using classical image

Linköping Studies in Science and Technology, Dissertation No.. 1862, 2017 Department of