Eindhoven University of Technology

MASTER

Proving correctness of threaded parallel executable code generated from models described by a domain specific language

Roede, S.

Award date: 2012

Link to publication

Disclaimer This document contains a student thesis (bachelor's or master's), as authored by a student at Eindhoven University of Technology. Student theses are made available in the TU/e repository upon obtaining the required degree. The grade received is not published on the document as presented in the repository. The required complexity or quality of research of student theses may vary by program, and the required minimum study period may vary in duration.

General rights Copyright and moral rights for the publications made accessible in the public portal are retained by the authors and/or other copyright owners and it is a condition of accessing publications that users recognise and abide by the legal requirements associated with these rights.

• Users may download and print one copy of any publication from the public portal for the purpose of private study or research. • You may not further distribute the material or use it for any profit-making activity or commercial gain TECHNISCHE UNIVERSITEIT EINDHOVEN

Department of Mathematics and Computer Science

Proving correctness of threaded parallel executable code generated from models described by a Domain Specific Language

By S. Roede

Supervisor & tutor

Dr. R. Kuiper (TU/e) Ir. J.M.A.M Gabriels (TU/e)

Eindhoven, July 2012 Computer Science and Engineering, Eindhoven University of Technology

Master thesis

Proving correctness of threaded parallel executable code generated from models described by a Domain Specific Language

Author: Graduation supervisor: dr. Ruurd Kuiper ing. Sybren Roede Graduation tutor: [email protected] ir. Joost M.A.M Gabriels

July 7, 2012 Preface

This thesis was written in partial fulfilment of the requirements for graduation in a master degree in computing science at the Eindhoven University of Technology. This graduation project was done in the area of Software Technology for the research group Software Engineering Technology. The author would like to give a special thanks to my graduation supervisor Ruurd and my gradu- ation tutor Joost. They provided an excellent job in supervising the research work and correcting the thesis. During the process they made useful critical remarks and give valuable suggestions to strengthen the work. They made my graduation project a joyful process and learningful experi- ence. I want to thanks K. Rustan M. Leino of the Research in Software Engineering (RiSE) group at Research. In an early stage of my graduation project he give me the change to use his expertise in the field of formal verification. My deep thanks go to Bart Jacobs of the Department of Computer Science of the Katholieke Universiteit Leuven, Belgium. The time he spend on me and the opportunity to learn from him is greatly appreciated. The verification in VeriFast could not been done without his guidance. The two afternoons in Leuven where very helpful and a great experience. The strong support and love of my friends and family is a blessing during my study. I am especially grateful to my family and lovely girlfriend who were tireless in motivating and supporting me to finish my master degree.

2 Abstract We investigate correctness of threaded parallel executable code generated from models described by a Domain Specific Language (DSL). There are challenges in developing correct multi-threaded code. Model Driven Engineering (MDE) is a promising approach: the mod- eling can be done at a high abstraction level and with a chain of model transformations the implementation code is generated. During my Capita Selecta, I made a realization of the code generation step of a particular MDE-chain, the transformation of state-machine like SLCO models into multi-threaded C# code. In the generation process, use of and inheritance from multi-threaded generic code was applied. The goal of the present research is to investigate formal specification and verification of the generic code. This was done by first making a small overview of available tools and selection of the best suited tool for the verification. After that, we performed a parameterized verification with VeriFast on an essential, non-trivial part of the framework. Finally we reflect on the lessons learned.

3 Contents

1 Introduction 6 1.1 Capita Selecta project ...... 6 1.2 Goals and approach ...... 7 1.2.1 Approach ...... 7 1.3 Overview ...... 8

2 Preliminaries 9 2.1 SLCO ...... 9 2.1.1 State machine ...... 9 2.1.2 Language constructs ...... 9 2.2 Toosm...... 12 2.2.1 Logging and state space ...... 17

3 Verification techniques 19 3.1 Hoare Logic ...... 19 3.2 Owicki and Gries ...... 20 3.3 Rely/guarantee ...... 20 3.4 Separation logic ...... 21

4 Verification tools 23 4.1 Tool introductions ...... 23 4.1.1 Spec# ...... 23 4.1.2 Code Contracts ...... 24 4.1.3 ...... 24 4.1.4 Chalice ...... 25 4.1.5 VeriFast ...... 25 4.1.6 ProPar ...... 26 4.2 Tool selection ...... 27 4.2.1 Spec# ...... 27 4.2.2 Code Contracts ...... 29 4.2.3 Dafny ...... 31 4.2.4 Chalice ...... 31 4.2.5 VeriFast ...... 32 4.2.6 ProPar ...... 33 4.3 Conclusions ...... 33

5 A closer look at Separation Logic 35 5.1 Basics ...... 35 5.2 Variables as resource ...... 36 5.3 Concurrency ...... 36 5.4 Semaphores ...... 37 5.5 Permissions ...... 38 5.6 Further reading ...... 39

6 A closer look at VeriFast 40 6.1 VeriFast features ...... 40 6.2 VeriFast IDE ...... 41 6.3 Verification example ...... 42

7 Verification of the communication channels 46 7.1 Abstraction ...... 46 7.2 Communication Channel ...... 47 7.2.1 Channel specifications ...... 47

4 7.2.2 Channel VeriFast specifications ...... 49 7.2.3 Channel VeriFast annotation ...... 51 7.3 Communication channel client ready ...... 53 7.3.1 Channel specifications ...... 54 7.3.2 Channel VeriFast specifications ...... 54 7.3.3 Channel VeriFast annotations ...... 57 7.4 Client program ...... 58 7.4.1 Client specification ...... 59 7.4.2 Client VeriFast specification ...... 59 7.4.3 Client VeriFast annotation ...... 63 7.5 Client program with multiple senders and receivers ...... 66 7.5.1 Client specification ...... 66 7.5.2 Client VeriFast specification ...... 66 7.5.3 VeriFast client annotation ...... 69 7.6 Client program with multiple senders and receivers and richer specifications . . . . 72 7.6.1 Client specification ...... 72 7.6.2 Client VeriFast specification ...... 73 7.6.3 Client VeriFast annotation ...... 76 7.7 Remark on verification ...... 82 7.8 Source code improvements ...... 82

8 Conclusions and future work 86 8.1 Verification techniques and tools ...... 86 8.2 Verification of the communication channel ...... 87 8.3 Practicability and usefulness of tool-based approach ...... 88 8.3.1 VeriFast experience ...... 88 8.3.2 VeriFast effort ...... 88 8.3.3 VeriFast capabilities ...... 89 8.3.4 Formal verification experience ...... 90 8.3.5 Verification approach ...... 90 8.4 Threats to validity ...... 90 8.5 Future work ...... 91

A Appendix: Channel verification 96 A.1 Communication Channel (Ia) ...... 96 A.2 Communication Channel - client ready (Ib) ...... 99 A.3 Client - version 1 (II) ...... 103 A.4 Client - version 2 (IIIa) ...... 107 A.5 Client - version 3 (IIIb) ...... 113 A.6 Modifications to the VeriFast libraries ...... 123

B Appendix: Spec# - multithread example 125

5 1 Introduction

A phenomenon in software engineering is that programs get bigger and more complex every year. In the last couple of years the emergence of concurrent programming received more attention. Programming those complex systems with concurrency is difficult: threading and synchronization can lead to a range of errors, e.g., race conditions and deadlocks. There are techniques available to help cope with these defects. Some techniques check programs after the code has been made, e.g. static analyzers (without running), dynamic analyzers (during running) and model checkers (on a mathematical representation of the program). With other techniques such as Model-driven Engineering (MDE), it is possible to tackle those defects before any code is made. By investigating models of the software en generating code from those models, it is possible to detect defects earlier in the software development traject. In most cases this means that less effort is required to fix the defects. With MDE, models are not used just for documentation and communication purposes, but are first-class artifacts. Developing programs depends on constructing models that capture the in- tended behavior and properties, and eventually generating the source code from those models. Constructing a model enables the developer to deal with difficult aspects at a less complex and higher level of abstraction. Transformations are used to create new models, source code, test scripts and other artifacts. Models, possible generated with model transformation from other models, can be used to check whether the executed behavior is compliant with the specified be- havior. Also other properties like deadlock freedom and the absence of race conditions can be checked. One question that remains is, whether the transformations are correctness preserving. In other words: If we have proven that certain properties hold in the model, how can we be sure those properties still hold in the generated source code? In a Capita Selecta project at the TU/e, the author provided a concrete instantiation of an MDE- style transformation chain; transformations were made to generate source code out of a model. There we gave a informal argument to the correctness problem. In this M.Sc. project, the goal is to apply formal verification to answer the correctness problem.

1.1 Capita Selecta project

As part of a Capita Selecta project [1] at the TU/e in 2011, the author implemented a chain of model transformations to transform a model in SLCO to threaded C# code. SLCO (Simple Language of Communicating Objects) [2] is a Domain Specific Language (DSL) based on state machines. SLCO provides constructs for specifying systems consisting of state machines that operate concurrently and communicate with each other. In SLCO, the structure of a system is modeled using special structure artifacts. The behavior is modeled by state machines. After transformation, the resulting C# code consists of model independent (generic) code, and model dependent (specific) code. The generic code contains the general concepts such as state machines, transitions, and channels. The specific code contains the actual behavior of these concepts for a specific case such that when the specific code inherits from the generic code, it becomes a fully functional program. A framework called Toosm (Threaded Object-Oriented State Machines) created in this project contains the generic code. The MDE transformations generate the model specific code. In this thesis, we refer to the whole solution (framework and model transformations) as Toosm and only differentiate when necessary. In the Capita Selecta project, only an informal argument was given for the correctness of the code resulting from the transformations. Although a detailed comparison of the model state space and execution logs of the resulting code provided a reasonably convincing argument for correctness, a

6 formal proof of the correctness is what is really required. This is what the present M.Sc. project is aiming to provide.

1.2 Goals and approach

Because of the separation of generic and specific code, it is not enough to prove only the cor- rectness of the model transformations since we would miss significant details about the generic code. The separation does however also has a clear maintenance advantage. The concepts and general workings of a state machine are located in one place and do not need to be generated for every instance. Because the same code is inherited from for every transformation, we can use verification to verify the correctness of the generic code just once. This strategy is significantly different from more common strategies used today in MDE that verify a specific instance or only the model transformations, whereas we attempt to verify all instances (within certain bounds) based on generic code. While investigating the correctness of the generic code, the focus will be on obtaining an auto- matically checked formal proof. Therefore, a suitable tool has to be found. We formulate three main research questions: A What is the current state of art in tool-based verification of concurrent, object-oriented code? • Which tools are available for verification of concurrent, object-oriented code? • What are the techniques used by the tools? • What restrictions do these tools have? • Which tool is best suitable for the verification of Toosm? B Can one of the tools verify the correctness of concurrent object-oriented code, especially the generic code of Toosm? • To what extend can we verify a generic part in the Toosm framework? C How practical and useful is the tool-based approach for verification? • What lessons can be learned from this approach?

1.2.1 Approach

The first stage of the research (question A) is spent on making a broad overview of the verification techniques and tools available for verification of code. We need to get familiar with the techniques to be able to categorize and compare the tools. From this broad overview we will select the most promising tools and investigate them further. We also will define tool requirement. Based on our problem and needs, these tools will be evaluated against our tool requirements in order to choose one tool for the next stage of the master’s project. Because we want to start the verification (question B) with a simple, but still close to the original, case, we make an abstraction from the Toosm communication channel. The communication channel is an important concept and contains a lot of the generic functionality we wish to verify. Based on this abstraction, the verification process will be done in three iterations. Each iteration consists of a three steps: the first is to specify the requirements of the communication channel. These requirements are not related to any tool and technique. The second step is to transform these requirements in a form that the chosen verification tool (verifier) understands. The last step is to iteratively run the verifier, providing additional annotations of the code needed to verify the correctness.

7 In the first iteration, we verify only the communication channel itself. In the second iteration the communication channel is used by a client program. We try to verify the complete program to see if the communication channel can be used in a multi-threaded setting. The client program will have only one sender and one receiver on the endpoints of the communication channel. In this verification we want to parameterize the number of sent and received messages, that is, we want to show that we can send and receive k messages. The third iteration is to have multiple senders and receivers. The number of senders and receivers will also be parameterized, thus having n senders and m receivers. We end up of having a program with a parameterized number of senders, receivers, and messages (with k sent and l received). Furthermore, the verifier has to prove deadlock freedom and the absence of race conditions for all iterations. The three iterations of the verification process are shown in figure 1.

Figure 1: Verification iterations

In the last stage (question C) we address the lessons learned from the verification process.

1.3 Overview

The rest of the thesis is organized as follows. In chapter 2 an overview of SLCO and Toosm is given. In Chapter 3 some verification techniques are outlined, this includes Owicki-Gries and Separation Logic. Chapter 4 gives an overview of some verifications tools we encountered during this project, and we compare them to make a well considered choice in tools for further use in the project. The verification technique chosen for this project, Separation logic, is explained further in chapter 5. The chosen verification tool, VeriFast, is explained in Chapter 6. In Chapter 7, we provide the verification of the Toosm communication channel together with the specification and limitations. The last chapter, 8, describes the conclusions of this project and practicality and usefulness is the tool-based approach for verification and VeriFast. Furthermore, the chapter describes threats to validity and possible future work.

8 2 Preliminaries

In this chapter we explain a few concepts which may not yet be known to the reader. We de- scribe what SLCO is and which constructs the language contains. We also introduce the Toosm framework, which was developed during the Capita Selecta project at TU/e.

2.1 SLCO

Van Amstel, Engelen and Van den Brand created SLCO [2], when investigating iterative DSL design. SLCO provides constructs for specifying systems consisting of objects that operate in parallel and communicate with each other. The structure and behavior of the objects is described in a state machine approach. In their paper [2], they created a model of a conveyor belt in SLCO to investigate different model transformations. One transformation is used to transform an SLCO model into a model described in the Parallel Object-Oriented Specification Language (POOSL) [3], for simulation and performance analysis. Another transformation is used to transform the model into a model that can be read by the model checker Spin [4]. The last transformation transforms the SLCO model into executable code in the language Not Quite C (NQC). NQC can be executed on the Lego Mindstorms platform [5], e.g., a physical conveyor belt system could be built from Lego parts. The different target models have different language characteristics, i.e., POOSL does not support asynchronous communication. To bridge the gap a number of transformations are defined that transform an SLCO model to a different SLCO model with equivalent observable behavior, e.g. synchronous communication channels to asynchronous channels or bidirectional channels to unidirectional channels.

2.1.1 State machine

State machines are well known for defining the behavior of (parts of) a system. SLCO has a textual model. As it is sometimes easier to give a graphical representation there is also a graphical model of SLCO and a graphical editor in the Eclipse environment [6]; their expressive power is the same and both can reflect the same model. In figure 2 we give a graphical representation of two state machines (Note that to keep the picture simple, this is not exactly the SLCO graphical representation). In the next section those two state machines are used to describe most language constructs of SLCO.

Figure 2: Communicating state machines

2.1.2 Language constructs

The textual representation of SLCO uses of a number of language constructs. We use the textual representations of parts of an SLCO model to illustrate the language constructs. Listing 1 we show a basic example of textual SLCO. With the use of this example we briefly explain a few language constructs, note that we have omitted two parts for readability, we will fill in those gaps in listings 2 and listing 3.

9 Listing 1: Textual SLCO of communicating state machines (1)

1 model PaperExample1 { 2 classes 3 SenderClass { 4 ports 5 Q 6 state machines 7 SenderStateMachine { 8 ... 9 } 10 } 11 ReceiverClass { 12 ports 13 P 14 state machines 15 ReceiverStateMachine { 16 ... 17 } 18 } 19 objects 20 sender : SenderClass 21 receiver : ReceiverClass 22 channels 23 Q_to_P (Integer, Integer, Boolean) async lossless from sender.Q to receiver.P 24 }

An SLCO model always has one model construct. The model consists of a number of classes, objects and communication channels between objects. Objects are instances of classes. A channel can be one of three types: synchronous, asynchronous and lossless, and asynchronous and lossy. A channel is connected to a port of an object, allowing all state machines of that object to communicate over the channel. A class consists of a set of variables, ports and state machines. A port consists only of an identifying name. The variables are of the type integer, Boolean or string and can have an initial value. A state machine is an object that has variables, nodes and transitions. Variables can be of type integer, Boolean or string and can have an initial value. A node can be the initial state, this is the starting location of the state machine, a final state, in this state a state machine may end, or a regular state. Transitions are relations between two nodes, a source node and a target node. The source node and target node can be the same node. A transition can have a trigger. This can be a deadline or a signal reception. If the amount of time specified by the deadline has passed or if a signal is received, the transition is enabled. The countdown for the deadline starts when the state machine enters the source state of the corresponding transaction. The signal reception can have an expression. This expression can be over the arguments and over the variables. The expression yields to a Boolean value indicating if the signal can be received. A transition can also have a guard. A guard is a Boolean expression over the variables of the class and state machine that must hold to enable a transition. Triggers and guards are used in the state machine definition in listing 2. This is the state machine definition of ReceiverStateMachine in listing 1. The receive trigger has an expression over it, the second argument has to be equal to the value 2 and also the guard requires that the first argument is positive. If both criteria are satisfied, the first argument is stored in the variable j and the third variable is stored in the variable a.

Listing 2: Textual SLCO of communicating state machines (2)

10 1 ReceiverStateMachine { 2 variables 3 Integerj=0 4 Booleana 5 initialA 6 finalB 7 transitions 8 A2A fromA toA{ 9 trigger 10 receive G(j,[[2]],a) fromP 11 guard 12 (j >= 0) 13 } 14 A2B fromA toB{ 15 trigger 16 after 1000ms 17 guard 18 (j >= 2) 19 } 20 }

When the transition is made from a source state to a target state, the effects of the transition are executed. Effects are statements such as assignments or signal sending. An assignment construct is an assignment of a value or an expression to a variable. Signal sending sends a signal over a channel which is connected to a port of the class. Sending signals can be done in a synchronous manner and an asynchronous manner, depending on the channel type. Channels are one-place buffers to which the sending party can assign a value, and the receiving parties can read from. When the buffer is full, sending is not possible and therefore the transition is not enabled. With sending over asynchronous channels, the sending party can transition to the target state after placing the signal, possibly with arguments into the buffer. With synchronous sending it is a bit trickier. First we have to distinguish two different cases, whether the sending is the first action of a the effects or not. In the first case, sending of a signal, possibly with arguments, can only be done if, and only if, the receiver can receive that signal, i.e., the corresponding name and the expressions over the arguments must also evaluate to true, otherwise the transition cannot be taken. In the second case, the send statement will wait until the receiving party picks up the signal, and then proceeds to the next action or state transition. The concept’s effects are presented in listing 3, which is the initialization of the state machine SenderStateMachine of listing 1.

Listing 3: Textual SLCO of communicating state machines (3)

1 SenderStateMachine { 2 variables 3 Integeri=1 4 initialC 5 finalD 6 transitions 7 C2C fromC toC{ 8 guard 9 (i <= 2) 10 effect 11 send G(i,2,true) toQ; 12 i := i + 1 13 } 14 C2D fromC toD{

11 15 guard 16 (i >= 3) 17 } 18 }

2.2 Toosm

In the Capita Selecta project [1] the Toosm (Threaded Object-Oriented State Machine) was build to translate SLCO execution in C#. Toosm is an environment that has a translation from SLCO to C# and has generic code for artifact out of SLCO, e.g. the state machines and communication channels, described earlier. Toosm can not be executed on it own. It requires code that is generated by the Toosm transformations and the Toosm framework. The Toosm transformations take an SLCO model as input and produce code in C#. The Toosm framework consists of common functionality, from this generic code the model specific code inherits. Figure 3 shows the process that transforms the SCLO model to executable code.

Figure 3: Toosm flow

The reason for choosing to store the common functionality in a framework and not generating all required code is reuse of code. It is well known that maintaining duplicated code is harder than code without duplication. Most classes of the framework realize a single concept defined by the specification such as a state, a transition, a communication channel, etc. Using inheritance, the common functionality is available in the generated code - except for the communication channel class: which is used as an object, it is instantiated not inherited. Figure 4 depicts the essential parts of the framework as a class diagram. In this project the decision was made to keep the transformation simple, therefore we mapped each language construct to a counterpart in the code. We could, for example, have encoded intelligence about having different code generated if the send effect is the first action of the transition or not; the transformation now is unaware of this and it is determined at in the code. In listing 4 we present a small impression of the transformation file, here the model artifact is used to generate a file and in that file the C# model class is defined. In the constructor first the communication channels are created, followed by the objects which are instantiations of class artifacts, the last step in the constructor is to join the objects and the communication channels to each other (the generateChannelObjectJoin is not presented).

12 Figure 4: Toosm framework class diagram

Listing 4: Generation of model class in Xpand2

1 DEFINE generateModelFOR Model FILE "mdl" + this.name + ".cs"-  2 using System; 3 using toosm.Framework; 4 5 namespace toosm 6 { 7 // The class that represent the model 'this.name ' 8 public class mdlthis.name  : Model { 9 10 // The default constructor 11 public mdlthis.name (): base ("this.name ") 12 { 13 // declarations for each channel with a type of Channel 14 FOREACH this.channelsAS cnl  15 Channels.Add("cnl . name", new Channel("cnl . name")); 16 Channels ["cnl . name"].SynchronousChannel = IF cnl.channelType== ChannelTypeEnum::sync  true ELSE false ENDIF; 17 Logger.Write("Channel: 'cnl . name'", LoggerMessageType.ChannelInit); ENDFOREACH 18 19 // declarations for each class 20 FOREACH this.objectsAS obj  21 Objects.Add("obj . name", new Classobj.class.name("obj . class . name","obj . name", this)); ENDFOREACH 22 23 //join the channel to the objects (classes with statemachines) 24 FOREACH this.channelsAS cnl EXPAND generateChannelObjectJoinFOR cnlENDFOREACH 25 } 26 } 27 } 28 FOREACH this.classesAS cls  EXPAND generateClassFOR cls ENDFOREACH 29 ENDFILEENDDEFINE

13 State machines are executed in concurrently in threads. Each state machine object has his own thread. A lot of code for this is in Toosm, only the model specific parts are in the generated code. Model specific parts, are the lines code that are model specific, like evaluating a specific guard. The first way of information sharing between Toosm state machines can be via an instantiation of the communication channel object; the two most important methods of the channel are send and receive. Those methods contain all synchronization logic for controlling the threads. A communication channel has two end points, a sender side and a receiver side, which are modeled with Toosm classes. A Toosm class can hold multiple state machines, but on the receiver side only one state machine at a time can peek at the content and decide if it wants to receive it; this decision is determined by the conditional signal reception and guard evaluation. A second way two state machines in the same class can communicate is via a class variable. To prevent race conditions a lock is placed around the access to this variable. This type of communication is always generated by the Toosm transformations. The result of the Toosm transformations on the SLCO model shown in listing 1, 2 and 3 is presented in next three listings. In the listings we have removed some empty lines, comments and logging commands for readability. In listing 5 we give the code which is generated for the model object. Here we create the objects, channels and join them according to the model. In listing 6 we give the code for the receiver class and state machine. Notice that we add the states and transitions of the state machine in the constructor of the class. For each guard evaluation of a transition a method that is responsible for its task is created, like trying to receive signals and evaluating variables. In listing 7 the code for the sender is given, here we can see how actions, like signal sending, are executed.

Listing 5: Model specific code: model class

1 public class mdlPaperExample1 : Model { 2 public mdlPaperExample1(): base("PaperExample1") 3 { 4 Channels.Add("Q_to_P", new Channel("Q_to_P")); 5 Channels ["Q_to_P"].SynchronousChannel = false; 6 7 Objects.Add("sender", new ClassSenderClass("SenderClass","sender", this)); 8 Objects.Add("receiver", new ClassReceiverClass("ReceiverClass"," receiver", this)); 9 10 Channels ["Q_to_P"].Object1 = Objects["sender"]; 11 Channels ["Q_to_P"].Object1.Ports.Add("Q", Channels["Q_to_P"]); 12 Channels ["Q_to_P"].Object2 = Objects["receiver"]; 13 Channels ["Q_to_P"].Object2.Ports.Add("P", Channels["Q_to_P"]); 14 } 15 }

Listing 6: Model specific code: receiver class class and receiver state machine class

1 public class ClassReceiverClass : Class { 2 public ClassReceiverClass(string classname, string objectname, Model parent ): base(classname, objectname, parent) 3 { 4 StateMachines.Add("ReceiverStateMachine", new smReceiverStateMachine( "ReceiverStateMachine", this)); 5 Init ();

14 6 } 7 } 8 9 public class smReceiverStateMachine : StateMachine{ 10 public smReceiverStateMachine(string name, Class parent): base(name, parent ) 11 { 12 AddState ("A", true, false); 13 AddState ("B", false, true); 14 AddTransition(States["A"], null, true, GuardA2A, null, States["A"]," A2A"); 15 AddTransition(States["A"], 1000, false, GuardA2B, null, States["B"], "A2B"); 16 17 BeginAtomicStep(); 18 Init (); 19 EndAtomicStep(); 20 } 21 protected override void Init() 22 { 23 Variables.Add("j", 1); 24 Variables.Add("a", new object()); 25 base.Init(); 26 } 27 28 private bool GuardA2A(object sender, smTriggerGuardEventArgs args) 29 { 30 SignalMessage signal = Parent.Ports["P"].Peek(this);//peek and lock 31 if (signal != null)// there isa signal 32 { 33 if ((signal.Name =="G") && (signal.Args.Length == 3))// correct signal name and arguments 34 { 35 if ((2 == (int)signal.Args[1]) && true)//conditional signal reception(trigger) 36 { 37 object oldSMj = Variables["j"]; 38 Variables ["j"] = (int)signal.Args[0]; 39 object oldSMa = Variables["a"]; 40 Variables ["a"] = (bool)signal.Args[2]; 41 42 if(((int)Variables["j"] >= 0) )//conditional signal reception(guard) 43 { 44 Parent.Ports["P"].RemoveLast(this);//remove and unlock 45 return true; 46 } 47 //signal not received place values back; 48 Variables ["j"] = oldSMj; 49 Variables ["a"] = oldSMa; 50 } 51 } 52 } 53 //The signal is not for this sm. 54 Parent.Ports["P"].Release(this);//release peek lock; 55 return false; 56 }

15 57 58 private bool GuardA2B(object sender, smTriggerGuardEventArgs args) 59 { 60 return(((int)Variables["j"] >= 2) ) && (args.Transition. DeadlineExpired);//Delay guard 61 } 62 }

Listing 7: Model specific code: sender class class and sender state machine class

1 public class ClassSenderClass : Class { 2 public ClassSenderClass(string classname, string objectname, Model parent) : base(classname, objectname, parent) 3 { 4 StateMachines.Add("SenderStateMachine", new smSenderStateMachine(" SenderStateMachine", this)); 5 Init (); 6 } 7 } 8 9 public class smSenderStateMachine : StateMachine{ 10 public smSenderStateMachine(string name, Class parent): base(name, parent) 11 { 12 AddState ("C", true, false); 13 AddState ("D", false, true); 14 AddTransition(States["C"], null, false, GuardC2C, ActionC2C, States[" C"],"C2C"); 15 AddTransition(States["C"], null, false, GuardC2D, null, States["D"], "C2D"); 16 17 BeginAtomicStep(); 18 Init (); 19 EndAtomicStep(); 20 } 21 protected override void Init() 22 { 23 Variables.Add("i", 1); 24 base.Init(); 25 } 26 27 private bool GuardC2C(object sender, smTriggerGuardEventArgs args) 28 { 29 return((int)Variables["i"] <= 2) ;// Expression guard 30 } 31 private bool ActionC2C(object sender, smActionEventArgs args) 32 { 33 bool result = true; 34 bool isFirstActionOfTransition = true; 35 result = result && Parent.Ports["Q"].Send(new SignalMessage("G", new object[]{(int)Variables["i"], 2, true}), this, isFirstActionOfTransition); 36 if (!result) return false; 37 EndAtomicStep(); 38 BeginAtomicStep(); 39 Variables ["i"] = (int)((int)Variables["i"] + 1); 40 return result; 41 }

16 42 43 private bool GuardC2D(object sender, smTriggerGuardEventArgs args) 44 { 45 return((int)Variables["i"] >= 3) ;// Expression guard 46 } 47 }

2.2.1 Logging and state space

In the Capita Selecta we used logging and generation of generation of a state space to give a reasonable convincing argument for correctness, here we briefly describe how this was done. The models execution can be presented as a state space, this state space can be generated with a model transformation out of the model. The model transformation was made by creators of SLCO. The state space depicting states with state information and state transitions with corresponding SLCO actions. This state space can be compared against the execution of the program. The code of the listings in the previous section can be executed, after adding the framework code, by initializing the mdlPaperExample1 class and calling the run method. When the code is executed it generates a log file where we can inspect important steps, for example guard evaluation and value changes of variables. Such a log can be visualized by a program especially written for this purpose, see figure 5. The program searches the log for a begin and end log-record and combines them into a single event. All events are presented on a time line, which supports zooming. This tool was immensely helpful during the development of Toosm.

Figure 5: Toosm visualization tool GUI

With this program it is also possible to execute the generated code a specified number of times. The log of each execution is saved and those logs can be used to generate a “state space” of the program. That generated state space can be compared to the state space calculated from the

17 source SLCO model. A generated state space of the example used in this chapter is given in figure 6.

Figure 6: Generated state space of the code from the SLCO model in this chapter

18 3 Verification techniques

Concurrent programs these days are used in many different areas. There are compelling reasons for executing code concurrently, for example, the computation of certain data can be done much faster using true parallelism than when using sequential computation. Also, concurrency sometimes better fits the intuition designing a program than sequential execution. Programs today grow in size and complexity, which makes the presence of defects in especially concurrent programs more likely and more difficult to detect. Techniques for verification have been developed since the 1950’s. Some techniques are based on testing or reasoning about the behavior of possible executions. These techniques help to find bugs and, for example, improve efficiency, but do not provide complete verification of correctness. Model checking achieves this completeness for programs that have an, essentially, finite state space. Many programs however cannot be verified in this manner, simply because the number of possible executions is too large (or even infinite). Especially for programs used in safety-critical situations we therefore turn to formal verification methods based on theorem proving to verify that the specification is satisfied. In this thesis, we aim to verify specific concurrent code, the generic part of the Toosm framework, in a compositional fashion, using theorem proving. Compositionality is important both to keep the verification manageable as well as to localize the effects of adaptations on the re-impementation, the re-specification and the re-verification. In this chapter we briefly discuss various theorem-proving based approaches, from simple formal verification for sequential programs to the compositional verification of concurrent programs that we need. This provides some context for our approach and also explains our choice of a particular logic and proof approach. The field started with Turing in the fifties and passed a milestone with Hoare’s approach in the sixties. In 1975 a significant extension was made by Susan Owicki and David Gries who invented a method for verification of concurrent programs [7]. This led to the further development of other techniques like the compositional rely-guarantee method for shared-variable parallelism [8] and, somewhat later, the Separation Logic approach [9] [10].

3.1 Hoare Logic

The origins of (axiomatic) reasoning about program verification can be traced back to Turing in the 1950s, and, in the early sixties, McCarthy’s Mathematical Theory of Computation. But the first useable approaches should be attributed to Floyd and Hoare. Floyd introduced asser- tional reasoning on flowcharts for proving partial (discarding termination) and total correctness (including termination). In Floyd’s method, partial correctness is proved by annotating each of the program’s control points with a mathematical specification, an assertion, on the state of the program that should hold whenever control is at that point. The program with the assertions leads to verification conditions, which can be checked by hand or with a theorem prover for the logic in which the conditions are expressed. For total correctness, termination is proved by special counting-down arguments in looping constructions, showing that the loop is finite. Hoare developed this method further into a syntax-directed approach dealing with while programs directly rather than with their representations as flowcharts. While programs are programs with a small grammar with only a few statements, but they can express powerful and complex programs. The key concept Hoare introduced in his logic is a formula in the form of a Hoare triple, {P }S{Q}, denoting that if the assertion P is true before initiation of program(fragment) S, then the assertion Q will be true upon S’s termination. An assertion P holds locally if it is either: • an initial assertion of a component and is implied by the precondition of the program, or

19 • if P is established by a preceding atomic action S with pre-assertion Q. This concept can be used in a compositional way, building up from individual program statements to whole programs: to verify {P }S{Q} nothing about the program around S needs to be known except that always when control arrives at S, P holds - and about P that then, if S terminates, Q holds. Hoare’s approach received a great deal of attention, and many Hoare-style proof systems dealing with various programming constructs have been proposed since then; it is still an inspiration for current research. Thanks to Floyd and Hoare we view programs as state transformers. Another significant contributor to this subject was Dijkstra, who, among other things, introduced in 1975 the idea of weakest precondition, i.e. predicate transformers that provided are the basis for today’s verification condition generators used in automated verification [11].

3.2 Owicki and Gries

Hoare logic was extended to concurrent programs by Owicki and Gries [7] [12]. The Owicki-Gries proof system represents the first and probably the simplest extension of Hoare logic to parallelism, with shared variables. They extended the logic with only one proof rule, for the parallel cobegin ... coend statement. In the same manner as Hoare, one breaks down the parts of the verification into simpler pieces. First, the sequential components of the program are annotated with assertions. The components are then verified separately, as in the sequential case: local correctness. Then it is shown additionally, that each assertion in the annotation of a component is invariant under the execution of other components: global correctness. An assertion P is interference free, if for each atomic action {Q}S taken from another component, {P ∧ Q}S{P }. An assertion holds if it holds both locally and is interference-free. The interference-freedom requirement is also the main drawback of this method: Owicki-Gries is not compositional: To perform the interference-freedom test for the assertions in some component we require information about the implementation of ALL concurrent components. As Lamport stated [13], this means that for a concurrent program with n statements, there are O(n2) verifica- tion conditions instead of the O(n) conditions needed for while programs with Hoare logic. The need to have knowledge about the inner workings of two components is a major drawback. Dijkstra formulated this in 1965 in Cooperating Sequential Processes [14] that processes are disconnected except for explicit communication, as follows: “We have stipulated that processes should be loosely connected; by this we mean apart from the (rare) moments of explicit intercommunication, the individual processes are to be regarded as completely independent of each other.”. This limits the class of concurrent programs for which verification is feasible to languages that have quite constrained communication primitives - our programs with channels and senders and receivers are more complex than can be feasibly handled by the Owicki-Gries approach.

3.3 Rely/guarantee

The idea to obtain a more compositional proof method for general parallel programs is to enrich the specification of each component with additional, but more abstract, information about the interaction with its environment during its execution. Jones introduced in 1983 his Rely/Guarantee verification method [8] [15] [16] for shared memory parallel programs that has this feature. Instead of having only the precondition and postcondition, Jones added two new predicates: a rely condition and guarantee condition. The rely condition specifies what the component, minimally, expects from the environment, i.e., that the rely condition expresses requirements on all atomic actions of the environment, describing the maximal interference the component can tolerate. The rely condition states these requirements in specification terms, meaning that we do not need

20 to know the implementation of the environment. Conversely, the guarantee condition expresses the task performed by that component, and how this task may influence the environment - also abstractly. As a result, the proof rule for parallel composition can be formulated in terms of the specifications rather than the implementation of the components (as in the Owicki-Gries case), i.e. the resulting proof method is compositional. Although the rely-guarantee method represents a significant step forward in the methodology of program verification, it does not make the Owicki and Gries method obsolete. In concurrent shared variable programs where processes require a lot of reading from and writing to the same shared variable, non-compositional methods are sometimes more successful than compositional ones, i.e., where precise information about the information is required, using the implementation may be easier than separately writing specifications that are equally detailed.

3.4 Separation logic

The rely/guarantee in fact satisfies our desire for a compositional verification method, able to prove functional properties about programs with complex communication via channels and senders and receivers. Nevertheless, we look further to a more recent development, Separation Logic. The reason is, that besides functional requirements, we are interested in dealing with deadlock and race conditions. Separation logic provides a novel way to handle shared resources that is very well suited to deal with these two issues. Separation Logic ca be used to treat channels, viewing channels as shared resources - and be stretched to also deal with functional properties. In this section we just introduce the essential, basic idea of Separation Logic to deal with shared resources; in section 5 we present how Separation Logic handles concurrency, in section 7 we present the specialization to our specific case, channels with senders and receivers. Orthogonal to compositionality of program parts, these is the issue of aliasing: decomposing the memory. A modern system for reasoning about program correctness that focusses on this issue is Separation logic [9] [10]. It was developed by Reynolds and O’Hearn. It is an extension of Hoare logic with a built-in notion of a resource, and is based on the logic of bunched implications (BI) [17]. They key concept behind Separation Logic is local reasoning. In 1972 Burstall [18] made the observation that separate program code which works on separate sections of the memory can be reasoned about independently. In Separation Logic, the specifications and proofs of a program component mention only the portion of memory used by the component, and not the entire global state of the system. Thus, only the parts of the memory needed for the reasoning are used, and all other parts remain unchanged. Its main application has been reasoning about pointer programs. Separation logic gives an elegant solution for the problem of aliasing, which is a common problem in verifying pointer programs. Aliasing happens when a single memory object is referenced by multiple pointers in different places. Separation logic keeps track of the memory usage, e.g. that a single memory object is aliased, and also handles deallocation of unused memory. As Separation Logic is a recent development, there are various versions of the logic with complementary features, but there is not yet a standard uniform presentation of all of these. In the initial version of the logic there is no support for object oriented programs and permission based variable access. Those concepts are important for our research, and fortunately they are provided as a number of extensions, described in various papers. Specifically, standard Separation logic focuses on the memory model, which in Separation Logic has a store and a heap. A store is a function mapping variables to values. The heap is modeled as a partial map from addresses to values. A heap with no entries is called the empty heap and is denoted as emp. A heap with an entry on address E and with content F is written as E 7→ F . If the value is unknown or we do not care about the value, we can write E 7→ , which is shorthand

21 for ∃v : E 7→ v. To reason about multiple values in a heap, two heaps can be combined, using a new symbol, separating conjunction. Heap h and g for which the address domains are disjoint, can then be combined as h ∗ g. It is now easy to express that a program is in a state where the program has two values a x and y with the values 2 and 7, x 7→ 2 ∗ y 7→ 7. To enable reasoning about the effect of the execution of program fragments on store and heap, another important requirement added to Separation Logic is the requirement of “tight specifica- tion”1. A specification {P }C{Q} is tight when all of the resources accessed by C are described in its precondition P , or acquired through explicit resource transfer. No other resources will be accessed or affected by C when executed from a resource satisfying P . The tight interpretation is essential for separation logic. Suppose a we have {P }C{Q}, then every resource that C modifies is in P and every resource not modified is not in P . Every resource not in P can safely be used in other parts of the program, thus can be safely used in other threads. One result of this tight interpretation is the frame rule, which allows a small specification to be embedded into a larger context. It captures that C changes only the values of variables present in predicate P , then it will not change values of other memory locations. This additional requirement on the reasoning, going beyond Hoare logic, enables using Separation Logic for compositional reasoning.

{P } C {Q} modifies(C) ∩ vars(R) = ∅ {P ∗ R} C {Q ∗ R}

The frame rules side-condition is inherited from Hoare logic. It is required because ∗ only describes the separation of heap locations and not variables2. This frame rule is proven sound in the context of Separation Logic [20]. It is now possible to reason about low level imperative programs in Separation Logic, for example, {x 7→ 2 ∗ y 7→ 7}x := 5{x 7→ 5 ∗ y 7→ 7}.

At this stage of our logic evaluation, Owicki-Gries, Rely-Guarantee and Separation Logic are all still viable options. We now consider verification tools to further guide our choice.

1“tight specification” is occasionally referenced in other papers as “precise specification”. 2O’Hearn [19] observes that this side-condition can be removed, which is shown in chapter 5.

22 4 Verification tools

This chapter gives a partial overview of verification tools that are available for formal verification of programs. The focus is on tools that use a form of axiomatic reasoning on code. The first part has two goals: one is perform an exploration into the field of verification tools in general, to get a good understanding of the today’s tools, the other to create a list of tools that are promising for our aims. The second part is a selection process: we select the tool which is best suited for our verification goal. Because producing a complete list of verification tools that is available is outside the scope of the project, we consider a limited number of tools. The reasons for looking at these tools are quite diverse. Some features were very concrete, like the programming language supported. But even such a feature would not be fully decisive: we might choose to re-write our code to fit a tool, if that tool was the only realistic option for other reasons. Also, for example, even just having contacts that have good experiences with a tool, or a tool being supported by a larger company, made us have a closer look at such tool. We report on these initial explorations, as they provide useful information about the field and also because these explorations shaped the criteria that we used to select tool. First we give a short introduction to the tools, in section 4.2 we state the requirements for the tools such that we can make a well-considered decision, and then evaluate the tools on these requirements. In section 4.3 we end with a conclusion together with the tool choice. The first four tools are from Microsoft, since Microsoft is the creator of C#, the programming language of Toosm. These are Spec#, Code Contracts, Dafny and Chalice. The RiSE group of has a number of projects that focus of formal verification. Those tools turned out not to be applicable for the research question, therefore a wider search wind was made, and more tools were investigated; they are VeriFast and ProPar.

4.1 Tool introductions

In the next section, we present the tools by means of short descriptions.

4.1.1 Spec#

Spec# [21] is an object-oriented .NET language with design-by-contract features for method pre- conditions and postconditions and object invariants and also a non-null type system. With a non-null type system a variable can not have the null value, it always needs a concrete value or reference. In many programming languages, like Java and C#, an Integer is defined as a non-null type. Microsoft Research designed it to explore the possibilities of formal program verification. Spec# permits specification and reasoning about object invariants even in the presence of callbacks and multithreading. It supports both dynamic and static checking of certain properties, although it is not always possible to check all properties in both settings. The Spec# programming lan- guage is an extension of the object-oriented language C# 2.0. The compiler allows the source to be compiled to an executable or library, with additional code and data to support dynamic checking while executing the code. Beside the possibility of dynamic checking, Spec# has a static program verifier, Boogie. Boogie generates all verification conditions from the Spec# code, with the use of the classical weakest-precondition calculus. An automatic theorem prover, standard it uses Z3, tries to prove all verification conditions to prove the correctness of the program or find errors in it. The theory behind Spec#, or what Microsoft likes to call it the Spec# methodology, is based on boolean logic. The verification conditions created by Spec# are operations and assertions on a heap. It uses ownership of objects to find possible race conditions, and keeps track of locking orders to detect deadlocks. In Spec# one can use the value of a variable that it holds on entering

23 a method, read when the precondition was evaluated, side by side with the current value in a postcondition. Programming language-wise and also with respect to the features offered for specification and verification this seemed a good candidate. However, although the Spec#project is not official discontinued, there is no more effort spent on it. Microsoft gave the code to the open source community, support is via an Internet forum, but the activities here are limited. In personal communication with dr. R. Leino [22] it became clear that the system is outdated compared to modern program verification tools. All this would seriously limit the value of our verification, especially as regards value for future research.

4.1.2 Code Contracts

Another verification tool of Microsoft Research is Code Contracts [23]. Code Contracts uses the same theory and backend (Boogie) as Spec#. For Code Contracts Microsoft took the lessons learned in Spec# and added some new requirement and insides. Code Contracts provides a language-agnostic way to express coding assumptions, called contracts, in .NET programs. The contracts take the form of preconditions, postconditions, and object invariants. As Code Contracts work on all .net languages, we have all the benefits of those frameworks, thus also threads and all required synchronization constructs. The specification, in the form of contracts, can be checked for validity by a static checker, the same as in Spec#. There is also the possibility for dynamic checking. Code Contracts bring the advantages of design-by- contract programming to all .NET programming languages. Microsoft states that Code Contracts is industry ready, and it is shipped with the latest ultimate edition of Microsoft Visual Studio EDI. While the Code Contracts approach and tooling were released over two years ago now, it seems not to be commonly used while programming in .NET today. A major difference between Code Contracts and Spec# is that there is no way of explicitly speci- fying loop invariants on loop bodies. The static verifier has mechanisms to derive loop invariants itself. Code Contracts seem a good candidate for our purposes.

4.1.3 Dafny

Another tool of the Rise group of Microsoft Research is Dafny [24]. Dafny is an imperative object- based language with built-in specification constructs. It has a lot of aspects in common with Spec# and Code Contracts. Dafny is designed to support the static verification of programs. In particular, it can be used to verify the functional correctness of programs. The language is imperative, sequential, supports generic classes and dynamic allocation, and has build-in specification constructs. The specifications include preconditions and postconditions, frame specifications (read and write sets), and termination metrics. A nice feature of Dafny is that it is able to prove termination, if the required annotation is supplied. The verifier is thus able to proof total correctness, a feature not all tools support. To ease the usage of specification, Dafny supports ghost variables, recursive functions, and types like algebraic datatypes, sets, and sequences. Specifications and ghost constructs are only used during verification; the compiler omits them from the executable code. The Dafny verifier is run as part of the compiler. While compiling the code, the code is checked against the specification. Boogie and the Z3 SMT solver again powers the verifier. Dafny thus is a user friendly, mature tool, and as such rapidly gaining acceptance. A significant, and for us show-stopping, limitation is, that it is developed for sequential verification instead of concurrent verification.

24 4.1.4 Chalice

Chalice is another experimental language and tool from Microsoft Research [25], it explores spec- ification and verification of concurrency in programs. Chalice supports contracts such as pre- conditions and postconditions and loop invariants. Chalice translates its input program, with the contracts, to the intermediate verification language Boogie for which verification conditions in first-order logic can be generated. Like Spec#, Code Contracts and Dafny these verification conditions can then be solved by the Z3 SMT solver. Chalice is object-based, it supports the creation of objects but does not support inheritance. A program consists of one or more classes, which contains zero or more methods and fields. Each method has, like the previous tools, a precondition and a postcondition. In the method body one can use constructs like assignment, conditional constructs and looping constructs. When a loop is used in Chalice, it is possible to specify a loop invariant. Chalice keeps track of fractional permissions for all memory locations, all objects stored in the heap. With those permissions the verifier can determine whether certain code can only read or read and write to that location of memory. A method can not do a read or write to the class fields, unless we mention the required permission in the precondition of the method. Permission of fields should also be returned to the method caller, via the postcondition, otherwise the field becomes inaccessible after the method has finished. Permissions can be less than the full permis- sion, a fractional permission, which allows only a read action on the particular memory location. Fractional permission are specified as an integer value between 1 and 100, where 100 means the full permission. We will later encounter permissions in the context of our chosen logic and verifier - we defer more detailed explanation till then. Chalice supports threads. A thread in Chalice starts executing a method of a class in a new execution trace. Starting a new thread will transfer all permissions mentioned in the precondition of the starting method to the new thread. At a later stage in the execution path of the original thread, the two threads can be joined again. At this point, the return value of the starting method is returned, and the postcondition from the method is assumed, all permissions mentioned in the postcondition are transferred back to the original thread. An important concept in concurrent programming are synchronization constructs. In Chalice, monitors are used for this. Every object can be used as a monitor. A thread can give its permission to a memory location up to the monitor. When the monitor is acquired, the permissions are transferred from the monitor to the thread and on releasing, the permission goes back to the monitor. The monitor can be shared via a fractional permission by multiple threads, and when those threads acquire the lock it can work on the shared memory location. The permissions held by the monitor are specified in an invariant in the object. This invariant can also be used as object invariant for the related object. As should be clear from the detailed description of Chalice, this is a serious candidate for our verification. The reason that we looked for further approaches and tools is, the lake on support for inheritance. Because the code of Toosm makes frequently uses the concept inheritance, support for this concept in the verification is desirable. Also, we wanted to look beyond what was offered by Microsoft.

4.1.5 VeriFast

Another research tool for verification of certain correctness properties is VeriFast [26] [27]. Veri- Fast is developed at the Department of Computer Science of the Katholieke Universiteit Leuven. VeriFast takes an annotated program as input. Annotations include method preconditions and postconditions and loop invariants in a form of Separation Logic. Originally developed for C

25 programs, the current version is also able to handle a subset of Java. VeriFast is able to process the Java grammar, but it also requires contracts on the methods of the standaard Java classes. The creators of VeriFast supplied a number of contracts for the standard Java classes but not all; only those they needed in the past for their verifications have contracts. Most papers and help documents are focused on the C part, this “weak point” is acknowledged by the creators. The tool reports either “0 errors found” or indicates the location of a potential error. If the tool reports “0 errors found”, this means 3 that the program • does not perform illegal memory accesses, such as reading or writing an object instance field before an object is instantiated, or after the object instance has been freed, and • does not include a certain type of concurrency errors known as data races, i.e. unsyn- chronized conflicting accesses of the same field by multiple threads. Accesses are considered conflicting if at least one of them is a write access. • complies with function contracts (preconditions and postconditions) specified by the pro- grammer in the form of special comments (known as annotations) in the source code. VeriFast verifies each function by stepping through it, while keeping track of a symbolic represen- tation of the relevant program state. It will have some assumptions about the current state of the program and also heap chucks that represent the data of the heap. The verification is done via a symbolic execution, using a Separation Logic-based representation of the memory. VeriFast, as the name indicates, can, for the properties it is designed to verify, produce a result very fast compared to other provers. So a preliminary verdict: promising tool for our purposes, downside may be that it is using complex Separation Logic and aimed for Java rather than the C# we use.

4.1.6 ProPar

The research tool ProPar [28] [29] (Prover of Parallel Programs) is a tool written in Python that supports verification of parallel programs that annotated in Owicki and Gries style. ProPar is developed by W. Wesslink and A. Mooij of the Technische Universiteit Eindhoven. ProPar checks the correctness of annotated programs by taking an annotated program as input, and generates proof obligations. The generated proof obligations are checked by the PVS proof checker. The proof checker validates the proof obligations or fails, just stating whether or not the program meets the specifications. A details of which proof obligations are proven and which not is given to give a beter feedback to the user. ProPar has a basic language which is close to the language constructs used by Owicki and Gries. The grammar of the language is not close to C#, most other tools have a grammar which is or is closer the C#. ProPar is in this area a outlier. We have not found any support for Object Oriented programming in the language, but the most basic language constructs as assignment, if, loop and parallel composition exists in the language. Between the language constructs one can place assertions, predicates on the systems state, to verify the systems state at that point. Also the way programs and assertions are expressed is completely different than the other tools do it. In ProPar one file specifies the structure of the program and all assertions, guards and statements are replaced by identifiers. A second file contains a PVS translation of the assertions, guards and statements. For repetition and parallel composition there is also a notion of invariants; invariants are in a way shortcuts to specify that a certain predicate holds for a certain part of the code, they hold at the beginning up to the end of the related code. A preliminary verdict is that usage beyond the TU/e community and long-term stability and main- tenance of the tool are quite unclear, also a factor that plays a negative role is the programming

3Stated by the VeriFast developers “There are a few known small reasons (known unsoundness) why the tool may sometimes incorrectly report “0 errors found”.”

26 language.

4.2 Tool selection

To make a well-considered decision which tool we select, we established four main tool require- ments: concurrency, object-oriented, language, availability and easy of use. If other aspects played a role at preferring or discarding the tool, they will be stated in the section about that tool. An essential requirement is that the tool supports some kind concurrency. Tools without this can not deal with our program and can not proof the absence of concurrency defects. We would only be able to prove whether some non-concurrent parts of the code in isolation meet some requirement, but it would be hard to say something about the whole system. Another requirement is some kind of support for dealing with objects. It would be nice to have support for a complete object oriented language. There are tools that support objects but do not support subtype polymorphism. Support for subtype polymorphism is important due to the regularly use in Toosm, but it is not mandatory for the verification we perform in this thesis, as there we only specify and verify classes, but do not address more advanced OO features yet. There are relevant things we can prove where we do not need subtype polymorphism. Recoding of the code to remove the subtype polymorphism is maybe possible, but this would require additional arguments why this code reflects the original code, and why the verification on this code is a valid verification for the original code. We focus on tools that support the programming language where Toosm is made in, C#. This would degrease the effort of rewriting the code into a different language. As our initial tool survey indicates that this requirement limits the tool choice dramatically, we keep an open mind for tools with different languages. The last issue is the tools availability and the ease of usage. Because program verification is, in the sense of formal verification, no common good in the software development world, the number of available tools can be limited. This in contrast to the available programming languages, IDEs, test tools and other programming supporting tools. The ease of use of the tool is important, mainly the effort needed to use the tool, especially as regards tools from the academic world. For those tools, it is known that the documentation is not always complete or hard to read. Therefore, those aspects play an important part in the tool selection. This is a difficult to weigh requirement: even by-chance contacts with experienced users might influence the choice.

Recap in sort: • Concurrency (mandatory); • Object-oriented (at least support for objects); • Language (preferably C#); • Availability and ease of use (rather “soft” requirement).

4.2.1 Spec#

As stated before Spec# uses a super set of C#, therefore the first three requirement are easily met. We now give Spec# a closer inspection, we want to get some experience in verification with Spec#. In the next example we show a simple Spec# class with three methods. The methods must calculate a simple mathematical formula, in this way we can see how to formulate the specification together with the coding. At the same time the we can see if the verifier is able to conclude some straightforward mathematical calculations.

27 Listing 8: SpecSharp summation example.

1 class Summation { 2 //most simple summation ofa polynomial expression 3 public static int SimpleSummation1(int m, int n) 4 requires 0 <= m && m <= n; 5 ensures result == n + 1 - m; 6 { 7 int som = 0; 8 inti=m; 9 while(i<=n) 10 invariant 0 <= i && i <= n+1;/* loop invariant*/ 11 invariant som == i - m;/* loop invariant*/ 12 { 13 som = som + 1; 14 i ++; 15 } 16 return som; 17 } 18 19 //simple summation ofa polynomial expression(special case of arithmetic series) 20 public static int SimpleSummation2(int n) 21 requires 0 <= n; 22 ensures result == ((n*n + n) / 2); 23 { 24 assert 0 <= n; 25 inti=0; 26 int som = 0; 27 28 while(i

28 56 // assertm <=i&&i <=n; 57 // assert som ==((i+1-m)*(i+m)/ 2); 58 } 59 // assertm <=i&&i <=n&& som ==((i+1-m)*(i+m)/ 2)&&i ==n; 60 // assert som ==((n+1-m)*(n+m)/ 2); 61 return som; 62 } 63 }

This class can be verified by Spec#’s static checker. This means the postconditions are valid and therefore, the method meets its specification. There is one comment to make about the third method: the commented assertion in the method are unprovable in contrast to the complete method. The first assertion follows directly from the loop guard, the second commented assertion from the loop invariant and the fact we increase i by one, and the third is clearly the same as the loop invariant. The same holds for the fourth and fifth commented assertion. This shows a potential weak point in the static verification (or the verification condition generator). We also investigated the verification of multithreaded programs. We found that verification of mul- tithreaded programs was hard, and where not able to have a working verification of multithreaded code. For a number of other factors this tool is not a good choice. The factors that played a role where: • Effort needed for the verification is great. • Documentation was sometimes not clear and there where no running examples on concurrent verification to show how this kind of verification could be done. • No further development by Microsoft of this tool. • Outdated. Newer tools succeeded this tool. • Other tools seemed to provide better change for the verification. In the appendix B we show a multithreaded program with locking and sharing of variables, which we tried to get verified. The rather simple example shows up to level which we got, it could be that with simple modification this program is verifiable, but in search of tool we could not get it to work.

4.2.2 Code Contracts

Code contracts can be seen as the successor of Spec#. This tool was therefore a natural candidate in the selection process. Because Code Contracts is shipped with the .NET framework 4.0, initial setup was not needed and the support was already built in the IDE, Visual Studio 2010. Because the languages targeted seemd to allow concurrency, we expected Code Contacts to support concur- rency, but when investigating it, it appeared that the support for this was limited or non-existing. On the Internet someone stated even, “... using Code Contracts ... with multithreading can be downright dangerous sometimes”4. So although the programming languages support concurrency, as we have the complete .NET functionality to our proposal, and one can use specification in the code, without good support for concurrency in the verifier, we can not use the approach for concurrency verification. A second disadvantage of Code Contracts is the design decision of Microsoft of leaving out explicit loop invariants. Inferring loop invariants automatically has it disadvantages. In the example of

4 It is common the specify the goal of a method in its postcondition. In the situation of fine grained locking and a postcondition, this can not be done, another thread could have changed the situation between end of locking and before checking the postcondition, in such a way the postcondition does not hold anymore.

29 listing 9, the first two methods seen in the Spec# summation example are coded in C# and Code Contracts.

Listing 9: Code Contracts summation example.

1 public static int SimpleSummation1(int m, int n) 2 { 3 Contract.Requires(0 <= m && m <= n); 4 Contract.Ensures(Contract.Result() == n + 1 - m); 5 int som = 0; 6 inti=m; 7 while (i <= n) 8 { 9 som = som + 1; 10 i = i + 1; 11 } 12 return som; 13 } 14 15 //simple summation ofa polynomial expression(special case of arithmetic series) 16 public static int SimpleSummation2(int n) 17 { 18 Contract.Requires(0 <= n); 19 Contract.Ensures(Contract.Result() == ((n * n + n) / 2)); 20 Contract.Assert(0 <= n); 21 inti=0; 22 int som = 0; 23 24 while (i != n) 25 { 26 //Contract.Assert(0 <=i&&i <=n);/* loop invariant*/ 27 //Contract.Assert(som ==((double)i*i+i)/ 2);/* loop invariant*/ 28 //Contract.Assert(i !=n);/* loop condition*/ 29 30 som = som + ( i + 1); 31 i = i + 1; 32 } 33 Contract.Assert(i == n);/* negation loop condition*/ 34 return som; 35 }

The static verifier verifies the method SimpleSummantion1, but can not prove correctness of the postcondition of the method SimpleSummation2. Adding assertions similar to the loop invariant of Spec# in the loop had not the effect that it becomes verifiable. The verifier then fails at the commented assertion of line 27. Apparently removing explicit loop invariants in code contracts made writing specifications easier, as coming up with good loop invariants is considered to be hard, but this design decision of Code Contracts comes at a high cost: If the loop invariant can not be correctly generated, then the functionality of the loop, and therefore also the method containing the loop, can not be proven. Due to the disadvantages of poor concurrency support and the akward design decisions described above. Code Contracts, after this closer look, turned out not to be applicable for us.

30 4.2.3 Dafny

Due to no support for concurrency, this tool will not be investigated further. Concurrency is a requirement we can not dispense with.

4.2.4 Chalice

At first sight Chalice seems a promising tool for our research question: the tool was designed to deal with concurrency problems like we have and it is able to prove functional specifications. Chalice supports concurrency via threads and this is for us important. One downside is that it is not object-oriented, but object-based which means we do not have inheriting. The code we want to verify heavily depends on inheriting, this is a potential problem. As suggested before, some of our verifications can do without it and it maybe possible to make a verification on redesigned code without subtype polymorphism, but this is not preferred. Another disadvantage of Chalice is that it has its own grammar for the programming and specifi- cation language. This means that we have to make a translation from C# to this language, this is not preferred. The third disadvantage is the usability and support. The crucial verification of multithreaded code with semaphores is hard, we tried to get help from the tools forum. Questions posted to the Internet forum of Chalice where left unanswered, this could be due to the question or to the limited activities there. While at first glance it looked promising, the disadvantages for us were too big, therefore this tool is not our first choice. In itself Chalice is a nice tool so to give an idea of how verification with Chalice works we give again the same simple summation program used previously but now in Chalice, see listing 10. Note that we were unable to code return values of methods, for this we use a trick with an additional variable to store the return value. The verifier has no problem verifying the three methods.

Listing 10: Chalice summation example.

1 class Summation { 2 //most simple summation ofa polynomial expression 3 var SimpleSummation1Result: int; 4 method SimpleSummation1(m: int,n: int) 5 requires acc(SimpleSummation1Result) && 0 <= m && m <= n; 6 ensures acc(SimpleSummation1Result) && SimpleSummation1Result == n + 1 - m; 7 { 8 var som : int := 0; 9 vari: int := m; 10 while(i<=n) 11 invariant 0 <= i && i <= n+1;/* loop invariant*/ 12 invariant som == i - m;/* loop invariant*/ 13 { 14 som := som + 1; 15 i := i + 1; 16 } 17 SimpleSummation1Result := som; 18 } 19

20 21 //simple summation ofa polynomial expression(special case of arithmetic series)

31 22 var SimpleSummation2Result : int; 23 method SimpleSummation2(n : int) 24 requires acc(SimpleSummation2Result) && 0 <= n; 25 ensures acc(SimpleSummation2Result) && SimpleSummation2Result == ((n*n + n) / 2); 26 { 27 vari: int := 0; 28 var som : int := 0; 29 30 while(i

4.2.5 VeriFast

VeriFast is advertised as a verification tool for concurrent programs, that can deal with race conditions and deadlocks as well as functionality. The first two items are very desirable for us, especially if the support makes the verification smooth and easy, the second we are willing to put effort into investigating the possibilities. One thing that seemed problematic is that VeriFast uses C or Java. This is a disadvantage, but

32 a translation from C# to Java is, in most cases, not that hard. An argument of correctness can easily be made, because the two languages are very similar and based on the same programming principles5. Java is, like C#, object oriented, so we can use code with inheritance. This is preferable to other techniques. While evaluating the tool the usability seemed to be very good. There is a lot of documentation and examples, most of them in the C language. The feedback of the creators was quick, and helpful allowing us to proceed to more daring problems. Our initial hesitance to go the direction of Separation Logic was assuaged when we found out that the part used to describe channels-as-a-shared-resource turned out to be quite manageable. All things considered, we have chosen this tool to proceed with. We therefore leave it at this point and in chapter 6 will give a more extensive description of VeriFast.

4.2.6 ProPar

This tool is not investigated further because another tool, VeriFast, which was promising was selected during the tool investigation. At first glance ProPar has a few disadvantages. The first is it seems to miss the support for object oriented programming. Secondly, the language required to encode the program in, is not close to C#. The last disadvantage is the documentation supplied with ProPar was very concise; because to I did not try to verify any programs; I can not say if this concise documentation is enough.

4.3 Conclusions

To conclude the tool selection, we present the tools and the four requirement in table 1. In the table a “1” means fulfilment of the requirement and a “0” means no fulfilment, we used a half to indicate fulfilment of the requirement is not complete.

Table 1: Tool comparison

Tool Concurrency Object-Oriented Language Availability and ease of use SpecSharp 1 1 1 0 Code Contracts 0 1 1 1 Dafny 0 1 0 1/2 Chalice 1 1/2 1/2 0 VeriFast 1 1 1/2 1 ProPar 1 0 0 1/2

In the last couple of sections we already excluded for different reasons all tools except one. The most promising tool at the end of this tool investigation is VeriFast, therefore we will continue this project with that tool as selected verification tool. We see differences in the power of the prover, even if the same backend and SAT solver is used. Spec#, Code Contracts and Chalice all convert their input to the intermediate language of Boogie and use Z3. The selected tool was most promising and had only one disadvantage the language, C# vs Java, but we feel like this can be overcome.

5some state that C# is inspired by Java or even an imitation of Java, others state it is evolved out C++ and a common runtime environment, lets leave that discussion as it is.

33 We see that verifications tools to do verification on annotate source code with concurrency is not yet a common good. We give here a small overview of tool we encountered but did not do any further research in due to different reasons: • VCC is an industrial-strength verification environment for low-level concurrent system code written in C. (Microsoft) Main disqualify reason is the non Object Oriented C language. • Smallfoot is an automatic verification tool that checks Separation Logic specifications of sequential and concurrent programs that manipulate recursive dynamically-allocated (linked) data structures. (Caldera Systems/The SCO Group) • VerCors is a verification tool for Concurrent Data Structures, its specification language combines features of Separation Logic with JML. (Universiteit Twente) Experimental tool, no open beta. • ESC/Java (Extended Static Checker for Java) is a programming tool that attempts to find common run-time errors in Java programs by static analysis of the program text. (Kind- Software Research Group at University College Dublin) Main disqualify reason, no releases the last 5 years.

34 5 A closer look at Separation Logic

In section 3.4, Separation Logic is introduced. This section repeats the basics and then provides the extensions for concurrency and permissions. This forms the basis for many verification tools that use Separation Logic and concurrency. This chapter includes a minor disclaimer. There a few notable extensions to Separation Logic. There us no claim that the techniques or extensions described here are the same as the techniques that drive VeriFast.

5.1 Basics

First we will repeat the most fundamental concepts demonstrated in section 3.4: • {P } C {Q} - Hoare triple, the precondition P is met, the command C establishes the postcondition Q;

• emp - empty heap; • E 7→ F (points-to) - a singleton heap with address E and contents F ; • E 7→ - shorthand for ∃v : E 7→ v;

• h ∗ g - (separating conjunction) a heap with two heap cells h and g; which do not point to the same memory location;

{P } C {Q} • {P ∗R} C {Q∗R} modifies(C) ∩ vars(R) = ∅ - frame rule.

• Tight specification (precise specification) - All resources required to execute the statement are present in the precondition of the statement and no more. Separation Logic is very well suited for reasoning about pointer programs; conversely, the standard Hoare logic rules are unsound when aliasing is allowed. Consider the following block of code x.f = 3; y.f = 4; {x.f < y.f}. The specification seems to hold, but in a language with pointers it may not be the case. Take for example the situation where both x and y point to the same memory location, then the assertions of the specification do not hold. The technical term of multiple pointers to the same memory location is called aliasing. Hoare logic has the Assignment axiom, {[E/x]P }x := E{P }, which is clearly is unsound if aliasing is allowed. In Separation Logic this situation can not occur, the frame rule disallows it, because there is a command that modifies a variable that is also free. An example of a small proof for a pointer program is shown in listing 11.

Listing 11: Separation Logic pointer example

1 { emp } 2 local x , y ; 3 { x 7→ ∗ y 7→ } 4 x = new Cell ( 3 , nil ); 5 { x 7→ 3 ,nil ∗ y 7→ } 6 y = x ; 7 { y 7→ 3 ,nil ∧ x ==y } 8 y . head = 4 9 { y 7→ 4 ,nil ∧ x ==y }

35 5.2 Variables as resource

In classical Separation Logic variables are separate from the heap. They form a stack, and cannot be pointed to; many rules need side conditions, e.g., the frame rule. Bornat et al. [30] showed that one can eliminate the variable-use side conditions on the rules: variables are treated as resources in the same way as heaps are treated as resources. This is a gentle change in the logic, but it permits Separation Logic to deal with operations which control access to variables. A resource is identified by a resource name, and a set of variables is connected to resource. The frame rule can now be expressed without the side condition:

{P } C {Q} {P ∗ R} C {Q ∗ R}

Note that this reduces the amount of work needed for verification by a great factor, because R can be of any form.

5.3 Concurrency

Reynolds insight of separating conjunctions, described in [10], and the idea of tight specification of Ishtiaq and O’Hearn [31]6, form the basis of Separation Logic. Bornat’s extension made it possible to reason much clearer with the frame rule. This basis is not sufficient to tackle our verification problem. Back to that, our problem requires us to be able to deal with concurrency. The need for parallel composition of commands, C1 k C2, is evident. O’Hearn also developed a form of the logic for dealing with concurrent programs [19]. Separation logic is very well suited to handle concurrency. The key idea is that threads (processes) can only access disjoint resources, and then we can verify the different processes as disjoint sequential programs. The definition of the (disjoint) parallel composition rule is as follows.

{P1}C1{Q1}{P2}C2{Q2} if Cidoes not modify any free variable in Pj,Cj,Qj for i 6= j {P1 ∗ P2}C1 k C2{Q1 ∗ Q2}

This rule says that if two threads have disjoint memory requirements, they can execute safely in parallel, and the postcondition is simply the composition of the two threads postconditions. We think of the memory location changed by the process Ci, which are specified in Pi, as being “owned” by process Ci. The other process can not interfere due to the side requirement of the parallel composition. In listing 12 a disjoint concurrency example is given, original presented in [19].

Listing 12: Separation Logic disjoint concurrency example

1 { x 7→ 3 ∗ y 7→ 4 } 2 { x 7→ 3 }{ y 7→ 4 } 3 x = 4 ; k y = 5 ; 4 { x 7→ 4 }{ y 7→ 5 } 5 { x 7→ 4 ∗ y 7→ 5 }

6The importance of this paper is huge, in 2011 ACM SIGPLAN awarded the 10-Year ’Most Influential POPL Paper’ award to Ishtiaq and O’Hearn for their paper they presented 10 years ago on POPL, it turned out to be ground-breaking work on Separation Logic.

36 This works well for verifying the rather trivial case of parallel programs without any shared variables, but how about communication between processes? It has been stated that a thread owns a heap or part of a heap, but now heaps can also be associated with a resource identifier. When a heap, e.g. a variable, is associated with a variable, it is described by an invariant. With the rule resource r (Ir) we establish that the variables described in Ir are owned by the resource r. The Conditional Critical Region rule, with r when G do c od, gives mutually exclusive access to the heap described invariant Ir. The execution of the command goes as follows, the thread waits for exclusive ownership of the resource identifier r. When the thread gets the exclusive ownership the guard G is evaluated. If G is false then the thread releases ownership of r and starts executing the command again. If G is true, then the thread executes C. Ownership of r is released when the execution of C terminates. Both C and G may refer to both the threads and the resource identifier resources.

{(P ∗ Ir) ∧ G}C{Q ∗ Ir} {P } with r when G do C od {Q}

Two important remarks have to be made here. First the rules imply partial correctness; the rule does not guarantee termination of the program, because we might never get into a situation that we have exclusive access to r. Second, the rule is only sound if P ∗ Ir, Q ∗ Ir and Ir are precise. This requirement reflects Dijkstra’s principle and slogan “Well specified processes mind their own business” [19]. In listing 13 we give a example where a shared resource is used, adapted from [19]. The buffer resource in line 5, takes both the variable full and c, they are not available any more until the ownership of buf is taken and the guard is evaluated to true, this happens at line 9. After this the RI is available again, and because it is known what the value of full is it is known which part of the resource invariant is true.

Listing 13: Shared variable via resource example

1 {emp} 2 local full = false , c ; 3 { f u l l 7→ f a l s e ∗ emp} 4 {RI ≡ (¬ f u l l ∧ emp) ∨ (full ∧ c 7→ ) } 5 resource buf ( c , full ) 6 {emp} 7 local m =”msg”; local n ; 8 {m 7→ ”msg” }{ n 7→ } 9 with buf when ¬full do with buf when full do 10 {RI ∗ m 7→ ”msg” }{ RI ∗ n 7→ } 11 { f u l l 7→ f a l s e ∗ emp ∗ m 7→ ”msg” }{ f u l l 7→ true ∗ c 7→ ∗ n 7→ } 12 c = m ; k n = c ; 13 { f u l l 7→ f a l s e ∗ c 7→ ”msg” ∧ c=m }{ f u l l 7→ true ∗ n 7→ ∧ c=n } 14 full = true ; full = f a l s e ; 15 { f u l l 7→ true ∗ c 7→ }{ f u l l 7→ f a l s e ∗ n 7→ } 16 {RI}{ RI} 17 od od ; 18 {RI}

5.4 Semaphores

A way to implement Conditional Critical Regions is by protecting the region with a semaphore. Semaphores were invented to be guards on ‘critical sections’ of code.

37 A common synchronization construct is the lock, we can model a lock with a semaphore. Acquiring a lock is used to guard the entrance to the section, and releasing it to exit the section. A semaphore can be set to allow a number of threads into the critical section, sometimes we call a binary semaphore, a semaphore of one, a lock. Lock acquiring can be accomplished with the with r when G do c od rule, as with s when s > 0 do s := s − 1 od; and lock releasing as with s when true do s := s + 1 od. The semaphore name s doubles as a resource name and a program variable. In listing 14 we give an example where locks are used, adapted from [19]. P and V are used for the acquiring and releasing of a lock; their definitions are at the bottom of the listing.

Listing 14: Semaphore example

1 {emp} 2 local free = 1 , busy = 0 , c ; 3 { f r e e 7→ 1 ∗ busy 7→ 0 ∗ c 7→ } 4 { R I f r e e ≡ (free=0 ∧ emp) ∨ (free=1 ∧ c 7→ ) } 5 {RIbusy ≡ (busy=0 ∧ emp) ∨ (busy=1 ∧ c 7→ ) } 6 resource free ( free ), busy ( busy ); 7 {emp} 8 while true do while true do 9 {emp}{ emp} 10 local m = produce (); P ( busy ) 11 {m 7→ }{ c 7→ } 12 P ( free ); local n = c ; 13 { c 7→ ∗ m 7→ }{ c 7→ ∧ n=c } 14 c = m ; k V ( free ) 15 { c 7→ ∧ m=c }{ emp} 16 V ( busy ); consume ( n ); 17 {emp}{ emp} 18 od ; od ; 19 {emp ∧¬ true }{ emp ∧¬ true } 20 { R I f r e e ∗ RIbusy ∗ f a l s e } 21 { f a l s e }

22

23 24 P ( s ) ≡ with s when s > 0 do s = s − 1 od ; 25 V ( s ) ≡ with s when true do s = s + 1 od ;

5.5 Permissions

Separation logic can now deal with two groups of variables, those are exclusively owned by a thread, which can be read or written, and those in mutual exclusion who are owned by a semaphore (temporary transfer of the ownership from the semaphore to a thread is possible). But there is a third group of variables, those of read only shared owned variables. It is easy to see that shared variables where the threads do not write to, can not lead to a race condition. To deal with those variables in Separation Logic, Boyland suggested a model with fractional permission. Ownership of a heap can be split into a number of fractional permissions, each of which allows a read only read access. Exclusive ownership can be written as E 7−→ F , and a fractional permission can be 1 written as E 7−→ F with 0 < z ≤ 1. Permission of a variable can be cut up into a number of z fractions, the sum of those fractions has to be 1. E 7−→ F ∗ E0 7−→ F 0, if E = E0 then they point z z0 to the same memory location, thus F = F 0 and z + z0 ≤ 1. In listing 15 we give a example where the permission of a variable is split into two read only

38 fractions, in this way the variable can be used in two concurrent process, adapted from [19].

Listing 15: Permissions example

1 {emp} 2 x = 7 ; 3 7−−→ { x 1.0 7 } 4 7−−→ 7−−→ { x 0.5 7 ∗ x 0.5 7 } 5 7−−→ 7−−→ { x 0.5 7 }{ x 0.5 7 } 6 y = x − 1 ; k z = x + 1 ; 7 7−−→ 7−−→ { x 0.5 7 ∗ y 7→ 6 }{ x 0.5 7 ∗ z 7→ 8 } 8 7−−→ 7−−→ { x 0.5 7 ∗ x 0.5 7 ∗ y 7→ 6 ∗ z 7→ 8} 9 { x 7→ 7 ∗ y 7→ 6 ∗ z 7→ 8}

5.6 Further reading

The thesis provides a concise description of Separation Logic and a few of its extensions; for further reading there are many papers on this subject, for example [32]. Separation Logic is still actively researched. One of the topics researched is to combine the compositional reasoning with local reasoning [33] [34] [35]. Also the tooling which uses separation logic as a base is actively researched, new experimental tools are emerging [36] [37] [38] [39] [40].

39 6 A closer look at VeriFast

This section 6.1 first presents the main features of VeriFast some features are easily related to the concepts given in the previous chapter. The VeriFast IDE is presented in the next section, section 6.2. As last we present in section 6.3 a simple verification example; that explains how to verify a program with the necessary specifications.

6.1 VeriFast features

The VeriFast Tool is a program verifier which uses Separation Logic as its basis. It can verify sequential and concurrent C and Java programs. The verifier can check for NullPointerException or ArrayIndexOutOfBoundsException. For concurrent programs it checks that the program does not contain data races (memory safety), and that locks do not create deadlocks. When the verification succeeds and it reports no error, the assertions and method contracts (preconditions and postconditions) are respected in every program execution. VeriFast is based on modular verification, symbolic execution and Separation Logic. The modular verification of VeriFast is crucial for achieving scalability of verification. Each method is checked in isolation, thus converging all possible traces from its precondition to it postcondition. When verifying a method, all method calls are replaced by the respective method contracts. This means that the precondition is asserted, and then the postcondition is assumed. If there are no verification errors in all methods, then the complete program is error free. In the verification, VeriFast executes the method body symbolically. The symbolic state represents an arbitrary concrete state. It will start with the symbolic state that satisfies the precondition, and checks whether these result in a state that satisfies the postcondition. During the verification process, VeriFast executes each method symbolically. Symbolic execution is like ordinary, concrete execution, except that symbolic values are used instead of concrete values. A symbolic state represents an arbitrary concrete state that satisfies a certain condition. At the start of a method it satisfies the precondition of that method. VeriFast explores all (feasible) paths through the code of the methods body, from the methods entry point to an exit point of the method, exit points can be the return statement or uncaught exceptions. At method exit points VeriFast verifies that the respective symbolic state satisfies the postcondition to the method. VeriFast employs Separation Logic as a basis logic, this allows VeriFast to handle aliasing of memory location in a natural manner. The use of the concept of ownership allows VeriFast to reach a conclusion about data races. VeriFast actually enforces correct use of variables; when the program writes to a memory location the program should have the correct permission. Making sure a write can only be done on a memory location, when no other thread can do a read on the location, leads to race condition free programs. An important feature of the approach is that VeriFast breaks down the program memory (heap) into separate chunks, the chunks are passed from method to method during method calls and returns, or distributed between concurrent threads. Within VeriFast it is possible to break down the ownership of a memory location into multiple permissions, or more precisely have multiple read-only permissions to a memory location. The broken down permissions are the so called, fractional permissions. The memory location can in this way be used in differed parts of the code. VeriFast enforces that all fractional permissions to a memory location combined give a full write permission; the sum of the fractions should not exceed one 7. A nice feature of VeriFast is the possibility to create custom predicates, with this data abstraction is possible. Specifications can be made more concise and abstract, for example, instead of repeating a partial specifications in multiple preconditions, the repeating part can be written down as a

7It is possible to write down fractions greater then one in VeriFast, improper use of this can lead errors in VeriFast (this is a bug).

40 predicate and the precondition uses only the predicate. Data abstractions like hiding private fields of an object can also be archived with predicates. VeriFast has also more advanced features like generics, inductive data types, fixpoint functions, specification lemmas and atomic spaces. For those features refer to the VeriFast [27] website, where these are explained in a number of papers.

6.2 VeriFast IDE

This section briefly presents the IDE of VeriFast, this IDE is very helpful for the developer. To make it easier for developers to diagnose verification errors, VeriFast has an IDE that supports symbolic debugging. That is, when verification fails, one can inspect the symbolic states encoun- tered during symbolic execution on the path to the failure. A screenshot of the IDE is shown in figure 7.

Figure 7: VeriFast IDE: debugging a faulty program.

41 First we shortly describe the other panels we see in the IDE. In the main panel, the editor, we can program the code, left to that we see the local variables together with its symbolic values, the symbolic store. The right bottom panel contains the heap chunks, heap chunks are field chunks or predicate chunks that represent the separated heap parts. This panel is referred to as the symbolic heap. The center bottom panel includes assumptions on the current execution path made over the local variables or heap chunks, or referred to as the path conditions. The bottom left panel shows the steps made by the verifier during the symbolic execution of the current execution path. When a step is selected in the steps panel, VeriFast shows which command is executed in the editor and also the symbolic store, the symbolic heap and the path conditions are updated to that selected state. Another feature of VeriFast is the “run to cursor”, with this a new execution is started running up to the command where the cursor is placed in the main panel. Viewing the state of the program when selecting a step and the possibility to run up to a certain point in the code, makes debugging an easier process. Notice in figure 7 first of all the red bar in the toolbar, showing the verification error. The red bar indicates that verification has failed, and the message suggests the reason for the failure. This bar turns green if the verification is successful. If verification fails, the location in the program where VeriFast encountered the failure is indicated in the editor by a double red underline. In the example, the postcondition is shown with such a double red underline. This indicates that verification of method deposit failed because, on one of the methods execution paths, VeriFast could not prove the postcondition. The next section explores why this verification fails.

6.3 Verification example

A simple example demonstrates the VeriFast specification and verification. This verification is a preparation for understanding the verification of chapter 6. This is a small non-concurrent trivial example of a bank account class. Chapter 6 presents more complex concepts when they are needed in the verification. There is a VeriFast tutorial available [41], which presents programming concept like loops, ar- rays, lists, generics, multithreading and specification concepts as predicates, lemmas, inductive datatypes, fixpoint functions, function pointers and permissions. There is also a paper [42] about verifying Java programs, but it covers a limited number of concepts like permissions, data abstrac- tion, inductive data types, fixpoint functions lemmas and inheritance. VeriFast can also be used to verify Java Card applications [43], Java Card applications are applications to be run securely on smart cards and similar small memory footprint devices.

Listing 16: Verifast bank example (1).

1 /∗@ 2 /∗ A predicate which represent the data structure Account. 3 ∗/ 4 p r e d i c a t e account(Account account, int balance, int limit)= 5 account.balance |−> balance& ∗& account.limit |−> l i m i t& ∗& 6 l i m i t <= balance;// an additional object invariant stating that the balance never drops below the limit

7 8 @∗/ 9 10 public class Account { 11 private int balance = 0 ; 12 private int limit = 0 ; 13 14 public Account ( int limit ) 15 //@ requires limit <= 0;//precondition

42 16 //@ ensures account(this, 0, limit);//postcondition ensures usa predicate witha balance of zero and provided limit. 17 { 18 this . balance = 0 ; 19 this . limit = limit ; 20 //@ close account(this, 0, limit); 21 } 22 }

In listing 16, we have defined a class Account for a small bank example. The class has two fields, one for balance of the account and limit field to set the maximum allowed amount that this account can be in depth. The account has one constructor function which sets the amount to zero and the limit to a certain amount. The specifications for this object are given inside a special annotation comment, a single line comment (//@) or a multi line comment (/*@ @*/). We represent the object in the verification by a single predicate, account. Each field of the class gets its own field predicate or heap chunk. The account predicate absorbs those field properties into the single account predicate. The account predicate also gives us the object invariant that the balance is never below the limit of account. Defining the object with custom predicates, instead of reasoning with the generated fields predicates, gives use the property of data abstraction. We describe the structure, and later also other properties, with a single predicate. This predicate can be used by specification of other objects, if the internal structure changes account we only need to change the predicate, instead of all places where the field predicate could have been used. The constructor method has a precondition. This is shown in the first annotation under the constructor head. The specification forces to supply a limit parameter which is at most zero. The postcondition guarantees that we end up with a predicate for this account with a balance of 0 and a given limit. The verifier executes the body and sets the field to 0 and limit. After that it closes the account predicate, this means that it takes the field predicates and puts them in the account predicate. This predicate is put into the heap store. If we change the zero, for example, in a one, of the balance on one of the three places, VeriFast will report a verification error. Depending on the place of the change, it can not prove the predicate “0==1” or find the predicate “account(this,0,limit)” in the heap store, it has only the produced predicate “account(this,1,limit)”. In listing 17, we extend the Account class with the methods getBalance which returns the current balance of the account, and deposit which adds a certain amount to the current balance. Both methods require the account predicate in the heap store, deposit has a second precondition that the deposit amount is not negative. If we would not require this, the condition “limit <= balance” in the account predicate can not be proven. In the open and close specification commands, the dummy pattern “ ” indicates that the balance and limit fields are insignificant. When opening the account, the dummy patterns are replaced by the symbolic symbols b and l from the precondition. Another choice we could have made is to use the bounded specification variables or programming variables. In that case, the variables need to have the correct value. The return keyword in the postcondition represents the return value of the method.

Listing 17: Verifast bank example (2).

1 public class Account { 2 //...

3 4 public int getBalance () 5 //@ requires account(this,?b,?l); 6 //@ ensures account(this,b,l)& ∗& result ==b; 7 { 8 //@ open account(this, , ); 9 return balance ;

43 10 //@ close account(this, , ); 11 }

12 13 public void deposit ( int amount ) 14 //@ requires account(this,?b,?l)& ∗&0 <= amount; 15 //@ ensures account(this,b+ amount,l); 16 { 17 //@ open account(this,b,l); 18 balance += amount ; 19 //@ close account(this, , ); 20 }

21

22 //... 23 }

As we already stated the verification in the screenshot of figure 7 failed; if we take another look at the code in the screenshot we will see why. The postcondition could not be reached in one of the execution paths; luckily for us this method has only one execution path. The postcondition of the deposit method stated that it requires a heap chunk where the balance of the account has been increased with value of the amount parameter. In the symbolic heap, there is a heap junk of the account predicate which represents this object in the memory space. The second parameter of this predicate represents the value of balance. The balance value has a symbolic value of the balance when the method started plus twice the value of the amount parameter. This is indeed not what the postcondition of the method requires, therefore we need to look at the programming code. In the code we see that the balance is increased with the doubled value of the amount parameter. Correcting this programming error would indeed result in an error free program, and the verification would succeed. We extend the Account class for a second time in listing 18. We add a method for transfer an amount to another account. The transfer can only take place if the end amount of the account does not drop below his limit. While the postcondition of this method is defined as one conjunctive predicate, the method body uses another manner and even another method to make the same conclusion.

Listing 18: Verifast bank example (3).

1 public class Account { 2 //...

3 4 public boolean TransferTo ( Account target , int amount ) 5 //@ requires account(this,?b,?l)& ∗& account(target,?b2,?l2)& ∗& target != null& ∗&0 <= amount; 6 /∗@ ensures account(this,(result?b − amount:b),l)& ∗& 7 account(target,(result? b2+ amount: b2), l2)& ∗& 8 r e s u l t ==(l <=b − amount);@ ∗/ 9 { 10 i f ( amount > MaxAllowdWithdraw ()) 11 { 12 return false ; 13 } 14 else 15 { 16 //@ open account(this,b,l); 17 balance −= amount ; 18 //@ close account(this, , ); 19 target . deposit ( amount ); 20 return true ;

44 21 } 22 }

23 24 public int MaxAllowdWithdraw () 25 //@ requires account(this,?b,?l); 26 /∗@ ensures account(this,b,l)& ∗& result ==b+(l ∗ −1);@ ∗/ 27 { 28 //@ open account(this,b,l); 29 return ( this . balance + ( this . limit ∗ −1) ) ; 30 //@ close account(this,b,l); 31 } 32 }

This demonstrates the first basic steps of verification in VeriFast, chapter 7 will show a more daring verification of multithreaded code.

45 7 Verification of the communication channels

This section presents the verification of an essential, representative part of the Toosm framework: the communication channel in a multiple sender/receiver setting. Using VeriFast, we verify the absence of race conditions and deadlocks. These are properties that VeriFast is known to be well- suited for. Beside these two properties we specify and verify some functional properties - this, in combination with concurrency, is a more novel application of VeriFast. We consider a single communication channel with on one side of the channel n sender parties and on the other side m receiver parties. The parallelism is in the multi-party use of the channels: each party will execute in his own thread. The channels themselves are independent of one another, hence treating a single channel suffices to verify the essential properties. Each sender party will send k messages through the communication channel, and each receiver party will receive l messages. Prove is that the program does not suffer from deadlocks, that there are no race conditions, that after execution n ∗ k messages are sent and m ∗ l messages are received, that not more messages can be received than have been sent, that all sent messages will be received and finnally that the number of messages in the communication channel is the number of sent messages minus the number of received messages. The sender has only one responsibility, it keeps trying to send a message until it succeeds, this will be done until it has send k messages. The receiver operates in a similar manner, it keeps trying to receive a message until it succeeds, this will be done until it has received l messages. The verification is done incrementally and modular. We essentially follow the steps-outlined from the introduction. The approach outlined is to consider a channel in isolation, then add one sender and one receiver using the channel, and finally treat the multiple user situation. In each step (except for the last one, where just VeriFast Annotation is added), we first present a description in a of pseudo-code and mathematical notation, of what the behavior of the part of the generic code should be: a general description. This easy to read description should credibility to the claim that indeed the intuitively required behavior is captured. Second, we provide the corresponding specification in the Separation Logic formalism that VeriFast uses and the Java code that is intended to satisfy this specification. This is a transformation from one formalism to another, so in principle amenable to formal verification. This however is not the aim of this research, so we again only informally argue this transformation. Our concern is, to verify that the Java code satisfies the VeriFast specification - this usually requires extra annotation, which is the third thing we provide at each step. We encountered some obstacles that prompted us to add additional steps to explain how we dealt with these, see figure 8. First we introduce a abstracted version of the communication channel in section 7.1. In section 7.2, we start with the communication channel class verification (ia). The specification and annotation of the channel in section 7.2 appeared unsuitable for verification of a program with senders and receivers, therefore we adapt the specification and annotation of the channel in section 7.3 (ib). In section 7.4 we give the first client program that uses the communication channel, with only one sender and receiver thread (ii). This client program is extended to support multiple senders and receivers in section 7.5 (iiia). Here we consider functional properties that hold after thread termination. Section 7.6 (iiib) we consider properties that hold during thread execution.

7.1 Abstraction

Our starting point is the source code of the communication channel in the C# language of the Toosm framework. In order to apply VeriFast, which operates on Java code, the C# code is translated to equivalent Java code. To ease the verification of the communication channels, we abstract from some aspects of the channel in this translation.

46 Figure 8: Verification iterations (detailed)

The first abstraction is that messages are objects in Toosm, we are interested in the general working of the channel rather than in the message content. Hence, messages are identified by a string value instead of a complete object with a name and parameters. Two messages with the same string value are considered to be the same message. Thus, instead of sending an object with a serialized string as ‘A(true, 5, “a few words”)’ we just send ‘A’. As we are only concerned with the sending and receiving methods/properties of the communication channel, the peek method for peeking at the content of the buffer in the channel is removed. Finally, as we only look at the communication channel and not at the other artifacts of the framework, we model the endpoints of the communication channel differently. Instead of having state machines that can do a send or receive calls on the communication channel, the end points can be of any type of class, a class can perform send as well as receive calls on the communication channel.

7.2 Communication Channel

We start out by considering the communication channel by itself, without senders and receivers. The channel is specified in an intuitively easy to understand mix of mathematical notation and pseudo code, then the specification is translated to a VeriFast specification of Java code, and finally the annotation is added to enable Verifast to perform the verification.

7.2.1 Channel specifications

This section describes the intended behavior of a single communication channel in an intuitive manner: a mix of mathematical specification and pseudo-code, presented in Channel specifica- tions.

47 This mathematical specification captures the following requirements. A channel should be able to hold a number of messages. It also should provide a way to add and remove messages: a send method and receive method. There should be no message losses, and the messages should be received in the same order as they are sent. The last is properly debatable in the real world, but in the setting of a single memory device this is reasonable. The data structure to store the messages is chosen to be a list8. Messages are only added at the end, and removed from the front, thus the list is in fact used as a queue. In line with SLCO, message storage is limited. If the queue is full the send method will return this status, as a Boolean value (false), and it is up to the sender party to handle this situation. The receive method will return nothing if the queue is empty. The channel will be used in a multi threaded environment, therefore the send and the receive operation are required to be done in atomic actions. For this purpose, a semaphore is included in the pseudo code: firstly to ensure the atomicity, secondly to make the access to the itemList conditional on having the semaphore. The communication channel can be viewed as a thread-safe limited non-blocking queue.

Channel itemList : List = [] queueMaxSize : integer sema : Semaphore = 1 #itemList ≤ queueMaxSize Send ( msg : String ) : bool acquire sema #itemList < queueLength → itemList = itemList ++[msg]; result = true #itemList ≥ queueLength → result = false release sema Receive ( ) : String acquire sema itemList == [] → result = null itemList == (x : xs) → itemList := xs; result = x release sema

General description Channel

To be able to argue the transformation of the mathematical specification into a VeriFast specifi- cation and Java code we list the verification properties in words: 1. Deadlock and race free. 2. Send/receive are the only operations that access itemList. 3. Send/receive are atomic. 4. Functionality of send/receive. 5. Numerical requirements on messages numbers.

8The queue datatype is not supported in VeriFast, support for this can be made, but we have chosen not to do this.

48 7.2.2 Channel VeriFast specifications

The next step is to take the mathematical specification of the channel and transform it to a form that VeriFast can handle. Rather than providing a formalism-to-formalism translation (which is not the focus of this re- search), we consider how the list of properties is reflected in the formalization. For readability, we have split the code over two listings, the first one containing the set-up of the channel, the second one the send and receive methods. Note, that some specification details in the first listing can only be appreciated in combination with (the explanations of) the second one. 1. The VeriFast approach does not explicitly specify absence of race conditions and deadlock, but guarantees this indirectly for every verified program. Race conditions can only occur when a variable is referred to from more than one place: aliasing. Aliasing of a variable means shared ownership. VeriFast requires that this is expressed with fractional permissions in the specification. VeriFast furthermore requires that fractional permissions to the same memory location sum up to at most one. VeriFast checks that writing to a variable only occurs when the writer has permission 1, thus avoiding race conditions. We use fractional permissions on line 3, 4 and 14 in listing 19. The variable queueMaxSize and the ghost variable itemsGhost are each split into two fractions, both have a 1/2 fraction, for both variables the sum of the fractions equals to one. Deadlock freedom is achieved by the structure of the program, we do not have to verify this. Because we have only one binary semaphore it is impossible to produce deadlocks: a deadlock requires to have at least two locks/semaphores. In VeriFast it is possible to prove deadlock freedom in programs with multiple locks9. Lock ordering is vital for this. Nested locks must always be obtained in the same order. This prevents the embrace deadlock. With additional specification this lock order can be incorporate into the specifications, and VeriFast tests if the acquiring of nested locks occurs in the correct order, thus preventing deadlock in the code. 2. The send and receive operations on the itemList should be the only ones that access the list. We formalize access to the itemList: itemList is added to the invariant of the semaphore, see line 3 in listing 19: channel.itemList |− > ?itemList This specifies that the imple- mentation of the send has to be such, that the semaphore is acquired when the send accesses the itemList. In the implementation this is indeed the case: sem.acquire(), line 10 and 28 in listing 20. The constructor method is an exception to this, and we can access the itemList there because the semaphore, and therefore the invariant, is not yet created. 3. The atomicity described in the general specification is achieved in the implementation by having the same acquire/release structure. 4. To specify the functionality of send, we need to refer to the itemList. However, essential in Separation Logic is that such a list has only one owner, in this case the semaphore is the owner, and hence the list cannot be referred to at the call of send: the semaphore is only acquired inside send. Therefore a trick is necessary: a ghost variable itemsGhost is introduced, its permission is split into two fractions (1/2, 1/2), one of which is added to the invariant and the other

9The support for proving the absence of deadlocks is there only for C programs, but the VeriFast libraries for Java can be extended with the same support.

49 used to specify the functionality of send. The invariant states that both lists have the same content. Adding one fraction to the invariant and requiring content equality is on line 3: itemList.List(?items) &*& [1/2]channel.itemsGhost |− > ?ghostItems &*& items == ghostItems in listing 19. Before stating the functionality of the send in the implementation, we introduce two pred- icates, Channel and ChannelState. Those two predicates represent this class in the verifi- cation. See line 13 and 14 in listing 19. The Channel predicate holds the semaphore, and therefore also the semaphore invariant, while the ChannelState predicate holds a fraction of the itemGhost variable and queueMaxSize. We use the introduced predicates to specify the functionality of the send in the implemen- tation. On line 5 of listing 20 we require both predicates, we bind the itemsGhost and queueMaxSize of the ChannelState to ?items and ?qms. Those two bind variables can be used to implement the functionality specification of the send method in the postcondition, see lines 6 and 7 in listing 20. The Boolean result indicating that if the buffer is not full, message adding can be done, is specified as: result == (length(items) < qms). The specification code that states that a message is appended to the list if there is room for it is specified as: ChannelState(length(items) < qms ? append(items, cons(msg, nil)) : items , qms). 5. The last property to be translated into VeriFast notation is the #itemList ≤ queueMaxSize. As this is an invariant over the itemList, it can be added to the existing semaphore invariant. This is expressed as a read-fraction of the queueMaxSize, and a function that determines the number of messages in a list, the length function. This specification is written at line 4: [1/2]channel.queueMaxSize |-> ?qms &*& length(items) <= qms in listing 19. The specification and the channel class without the methods are given in listing 19.

Listing 19: Channel class setup

1 /∗@ 2 p r e d i c a t e c t o r channel sema inv(Channel channel)()= 3 channel.itemList |−> ?itemList& ∗& itemList.List(?items)& ∗& [1/2]channel. itemsGhost |−> ?ghostItems& ∗& items == ghostItems& ∗& 4 [ 1 / 2 ]channel. queueMaxSize |−> ?qms& ∗& length(items) <= qms; 5 @∗/ 6 7 public final class Channel { 8 //@ list itemsGhost; 9 List itemList ; 10 Semaphore sema ; 11 int queueMaxSize ; 12 13 //@ predicate Channel()= sema |−> ?sema& ∗&[ ]sema. Semaphore( channel sema inv(this)); 14 //@ predicate ChannelState(list items, int qms)= [1/2]itemsGhost |−> items& ∗& [1/2] queueMaxSize |−> qms; 15 16 public Channel () 17 //@ requires true; 18 //@ ensures Channel()& ∗& ChannelState(nil, ); 19 { 20 itemList = new ArrayList (); 21 sema = new Semaphore ( 1 ) ; 22 }

23

24 //...

50 25 }

The specification of the methods and their bodies are given in listing 20.

Listing 20: Channel class specification

1 public final class Channel { 2 //...

3 4 boolean send ( String msg ) 5 //@ requires Channel()& ∗& ChannelState(?items,?qms); 6 /∗@ ensures Channel()& ∗& result ==(length(items) < qms)& ∗& 7 ChannelState(length(items) < qms? append(items, cons(msg, nil)): items , qms); 8 @∗/ 9 { 10 sema . acquire (); 11 boolean result ; 12 i f ( itemList . size () < queueMaxSize ) { 13 itemList . add ( msg ); 14 result = true ; 15 } else { 16 result = f a l s e ; 17 } 18 sema . release (); 19 return result ; 20 }

21 22 Object receive () 23 //@ requires Channel()& ∗& ChannelState(?items,?qms); 24 /∗@ ensures Channel()& ∗& ChannelState(tail(items), qms)& ∗& 25 r e s u l t ==(items != nil? head(items): null); 26 @∗/ 27 { 28 sema . acquire (); 29 String result ; 30 i f ( itemList . size ( ) == 0) { 31 result = null ; 32 } else { 33 result = ( String ) itemList . remove ( 0 ) ; 34 } 35 sema . release (); 36 return result ; 37 } 38 }

7.2.3 Channel VeriFast annotation

VeriFast can not verify whether or not the postcondition holds from just the precondition and the source code: it requires additional annotations in the body of the method to reach a conclusion. In listing 21 we give the class including its specifications and annotations. To explain the ideas behind the extra annotation we make a few remarks. The first one is that most ghost statements are open or close statements. A predicate is a named and parameterized assertion, enabling more concise contracts and data abstraction. Predicates can be be used to bundle several heap chunks (see section 6.1 into one heap chunks ). To open such a bundle,

51 the ghost statement open is used. This statement consumes the abstract predicate and then produces its body, e.g., it creates the separate heap chunks from a bundle. The close ghost statement consumes the predicates body and then produces the abstract predicate, e.g., it creates the bundled heap chunk from the separate heap chunks. Secondly, a ghost variable can not be updated by the regular source code, hence the three ghost field updates that appear in lines 10, 34 and 62. The third remark is that there are some special ghost statements pertaining to the semaphore. The semaphore creation requires a special predicate that states that the invariant of the semaphore can be used, in our case, one time. The one time ghost statement, on line 12, consumes the semaphore invariant and generates this special predicate. After semaphore creation, in the constructor, the semaphore usually is acquired there - in our case, this happens in a different method, the send method. Intuitively, to obtain the semaphore’s invariant in this method we need to “leak” the semaphore handle predicate: see line 14. The acquire in the send method, line 28, uses the semaphore handle predicate, the one that was leaked in the constructor, this handle is produced by the ghost statement on line 27. The last remark is about line 64, the switch state introduced there is a VeriFast technicality, having to do with taking the tail of an empty list, which we do not further elaborate.

Listing 21: Channel annotation

1 public final class Channel { 2 //...

3 4 public Channel () 5 //@ requires true; 6 //@ ensures Channel()& ∗& ChannelState(nil, ); 7 { 8 itemList = new ArrayList (); 9 //queueMaxSize= 2; 10 //@ itemsGhost= nil; 11 //@ close channel sema inv(this)(); 12 //@ one time(channel sema inv(this)); 13 sema = new Semaphore ( 1 ) ; 14 //@ sema. leakHandle(); 15 //@ close Channel(); 16 //@ close ChannelState(nil, ); 17 }

18 19 boolean send ( String msg ) 20 //@ requires Channel()& ∗& ChannelState(?items,?qms); 21 /∗@ ensures Channel()& ∗& result ==(length(items) < qms)& ∗& 22 ChannelState( length(items) < qms? append(items, cons(msg, nil)): items, qms); 23 @∗/ 24 { 25 //@ open Channel(); 26 //@ open ChannelState(items, qms); 27 //@ sema. makeHandle(); 28 sema . acquire (); 29 //@ open channel sema inv(this)();

30 31 boolean result ; 32 i f ( itemList . size () < queueMaxSize ) { 33 itemList . add ( msg );

52 34 //@ itemsGhost= append(items, cons(msg, nil)); 35 result = true ; 36 } else { 37 result = f a l s e ; 38 } 39 //@ close channel sema inv(this)(); 40 sema . release (); 41 //@ close ChannelState( , ); 42 //@ close Channel(); 43 return result ; 44 }

45 46 Object receive () 47 //@ requires Channel()& ∗& ChannelState(?items,?qms); 48 /∗@ ensures Channel()& ∗& ChannelState(tail(items), qms)& ∗& 49 r e s u l t ==(items != nil? head(items): null); 50 @∗/ 51 { 52 //@ open Channel(); 53 //@ open ChannelState(items, qms); 54 //@ sema. makeHandle(); 55 sema . acquire (); 56 //@ open channel sema inv(this)(); 57 String result ; 58 i f ( itemList . size ( ) == 0) { 59 result = null ; 60 } else { 61 result = ( String ) itemList . remove ( 0 ) ; 62 //@ itemsGhost= tail(itemsGhost); 63 } 64 //@ switch(items) { case nil: case cons(i0, is0): } //this trick is needed 65 //@ close channel sema inv(this)(); 66 sema . release (); 67 //@ close ChannelState( , ); 68 //@ close Channel(); 69 return result ; 70 } 71 }

With this annotation, the Channel class is verified by VeriFast as conforming to the specifica- tion. This means that no race conditions or deadlocks occur and that the methods respect their contracts. Thus the Send method adds the msg parameter to the queue of messages if the queue is not full, and the Receive methods removes the first element of the message queue and returns this element if the message queue is not empty. Another important property that is proven is that the message queue, the itemList, is “protected” by the semaphore, meaning that threads who want to write to this variable should have acquired the semaphore, thus ensuring that no racing on this shared variable can occur.

7.3 Communication channel client ready

This first set-up of the communication channel, in section 7.2 was appropriate for stand alone verification of the channel class. This setup can not be used directly in a verification of a program with clients like we want, see 7.3.2. Clients are objects which use the channel class, in our example a program with two end point objects. The changes only affect the VeriFast specification and the

53 VeriFast annotation: the general specification as well as the Java source code for the channel remain the same. The new VeriFast specification/annotation for use with the clients is called “client ready”. The preparation of the specification and annotation for use with clients is modular in the sense that the clients are only specified and not implemented: for the verification of the channel this suffices. The channel specification will now be prepared for modular verification in the context of clients that send and receive messages. This preparation is necessary, because the specification of the channel in isolation does not take into account the setting with clients. VeriFast requires some assumptions about clients. The ChannelState predicate in the specification of the channel in used in the, as yet unspecified, Client invariant of the Client specification. In the specification of the channel information from the Client invariant, about the queue content, is required. To preserve modularity, we specify (but not implement) four lemma functions in the channel specification, that provide the information from the Client invariant. Note, that these lemma functions are used as assumptions in the channel specification and, when they are implemented in the Client specifications, they have to satisfy these specifications. Note, that the requirements that are specified about the context that the channel is used in are very mild: they do not specify the clients that use the channel, but only some of the properties they should have to properly function with a channel. In the next section, 7.4, the client is specified much more precisely.

7.3.1 Channel specifications

The general description, given in 7.2.1 of the communication channel does not need to change.

7.3.2 Channel VeriFast specifications

The source code of the channel class does not need to change. The same holds for the channel invariant, ghost item list, Channel predicate and ChannelState predicate. The changes required are due to the fact that in the client program there are requirements about the state of the content of the channel during the execution: #queue = #sent − #received. Because of this kind of requirements changes are required to the specification out of section 7.2. The client specification can not do this with the specifications of section 7.2, but it can with the specification of this section. To be able to deal with this kind of client requirements the ChannelState must be added to a client invariant. This is further complicated by the, in itself desirable, compositional approach. Then two issues are to be dealt with. First, the client invariant must be shared among threads. In that case, no thread has write permission to objects that are referenced in the client invariant. However the ChannelState needs to be updated, for example in the send method of the channel, so the send method requires write access. This conflicts with the loss of the write permission. The remedy is the use of atomic spaces. Atomic spaces can be shared within a program, thus also among threads. On items in the atomic space we may (only) apply atomic actions. (Even if the atomic space is read only, we still may update an item in the atomic space with an atomic action.) Atomic actions are defined as lemma functions, with a precondition and a postcondition.

54 So the client invariant is put in an atomic space. Which is required by the send method. In the send method an atomic action will update the ChannelState in the client invariant. There is a second problem with adding the ChannelState to a client invariant. This is a conse- quence of the modular approach, where first the client ready channel is verified without the client implementation. At this moment the knowledge about what this client invariant looks like does not yet exist (this will be shown in the next section, in the client specification). The atomic action in the send method requires possessing this knowledge, in the send method the ChannelState needs to be separated from the rest of the client invariant. This can not be done without this knowledge. To remedy this, we state the existence of a lemma function that is able to separate the ChannelState from the rest. We emphasize that the rest has a unknown structure. We specify the lemma func- tion with a pre and postcondition, but we do not give the body that does the actual separating (this code is not yet available, we have not yet made a client invariant!). This lemma function can be used to do the separating: remember that in VeriFast function calls asserts the precondition and then assumes the postcondition without executing the body. This body needs to be given in client verification to verify the steps. For similar purposes, the inverse of the separation lemma function is needed. We start with the second issue. We specify the separation lemma functions, see listing 22, here the two lemma function type declarations are presented: The first is for the separating, the second is for its inverse. The separating predicate, channel sep, requires a inv predicate and a sepPred predicate, the inv is the client invariant. The lemma function will produce the ChannelState and the rest of the invariant. The rest is bundled into a parameterized unsepPred predicate.

Listing 22: Channel specification typedef lemmas (1)

1 /∗@ 2 typedef lemma void channel se p(predicate() inv, Channelc, predicate() sepPred, p r e d i c a t e(list , int) unsepPred)(); 3 r e q u i r e s sepPred()& ∗& inv(); 4 e n s u r e sc. ChannelState(?items,?qms)& ∗& unsepPred(items, qms); 5

6 typedef lemma void channel unsep(predicate() inv, Channelc, predicate() sepPred, predicate(list , int) unsepPred)(); 7 r e q u i r e s unsepPred(?items,?qms)& ∗&c. ChannelState(items, qms); 8 e n s u r e s sepPred()& ∗& inv(); 9 @∗/

Still in the context of the second issue, we need two other lemma type declarations, beside the separation lemma type declaration and its inverse, namely: a send and receive lemma type declarations. Those lemmas are needed, because of the unsepPred( , ) predicate. This predicate makes up the rest of the client invariant. This predicate also contains the items (see line 4 of listing 22). The unsepPred predicate is not yet defined which, like the client invariant, is not possible here. We need a way to update the items in the unsepPred predicate. For this we apply the same strategy as for the separating lemma function, by defining the lemma function with its pre and postcondition. In the client verification we need to give the body to verify this step. In listing 23 the two type declarations for the send and receive lemma function are presented.

Listing 23: Channel specification typedef lemmas (2)

1 /∗@ 2 typedef lemma void channel send(predicate() inv, Channelc, predicate(list < Object >, int) unsepPred, String msg, predicate() pre, predicate(boolean) post)(booleanr);

55 3 r e q u i r e s pre()& ∗& unsepPred(?items,?qms); 4 e n s u r e s post(r)& ∗& unsepPred(r? append(items, cons(msg, nil)): items, qms) ;

5

6 typedef lemma void channel r e c e i v e(predicate() inv, Channelc, predicate(list < Object >,int) unsepPred, predicate() pre, predicate(Object) post)(); 7 r e q u i r e s pre()& ∗& unsepPred(?items,?qms); 8 e n s u r e s post(items != nil? head(items): null)& ∗& unsepPred(tail(items), qms); 9 @∗/

The VeriFast concepts used here are atomic spaces[44] and lemma function pointer predicates[45]. Now with those new lemmas, we can specify the channel; presented in listing 24. The Channel predicate is fractionized because multiple clients will use the channel class. The methods also need the client invariant, which is in the atomic space. In this method we require the separation lemma function, its inverse and the send function. The required specifications can be made because we have defined their types.

Listing 24: Channel specification

1 public final class Channel { 2 //...

3 4 public boolean send ( String msg ); 5 /∗@ 6 r e q u i r e s 7 [?fc]Channel()& ∗&[?fa]atomic space(?inv)& ∗& 8 i s c h a n n e l s e p(?sep, inv, this,?sepPred,?unsepPred)& ∗& 9 i s c h a n n e l u n s e p(?unsep, inv, this, sepPred, unsepPred)& ∗& 10 i s c h a n n e l s e n d(?sendClientOp, inv, this, unsepPred, msg,?pre,?post)& ∗& 11 sepPred()& ∗& pre(); 12 @∗/ 13 /∗@ 14 e n s u r e s 15 [fc]Channel()& ∗&[fa]atomic space(inv)& ∗& 16 sepPred()& ∗& post(result); 17 @∗/ 18 19 public String receive (); 20 /∗@ 21 r e q u i r e s 22 [?fc]Channel()& ∗&[?fa]atomic space(?inv)& ∗& 23 i s c h a n n e l s e p(?sep, inv, this,?sepPred,?unsepPred)& ∗& 24 i s c h a n n e l u n s e p(?unsep, inv, this, sepPred, unsepPred)& ∗& 25 i s c h a n n e l r e c e i v e(?receiveClientOp, inv, this, unsepPred,?pre,?post)& ∗& 26 sepPred()& ∗& pre(); 27 @∗/ 28 /∗@ 29 e n s u r e s 30 [fc]Channel()& ∗&[fa]atomic space(inv)& ∗& 31 sepPred()& ∗& post(result); 32 @∗/ 33 }

In this verification we state the functionality requirement of the send method in a indirect way, via the unsepPred predicate and the channel send and channel unsep lemma type declarations.

56 In the channel send we state the postcondition of the send. The function has a Boolean value r, which is used to indicate if the Java update of the itemList was successful. If so the items in the unsepPred predicate also require an update (see line 4 of 23). The channel unsep lemma function must be called in the send atomic action, to update the items in the unsepPred predicate. The channel unsep lemma function requires that unsepPred items and the ChannelState items are the same to be able to produce the client invariant. This requirement in fact checks if we updated the state in the ChannelState (see line 7 of listing 22).

7.3.3 Channel VeriFast annotations

Modifications to the annotations in the bodies of the methods are also required. One change already mentioned is the atomic action on the ChannelState. In listing 25 we present10 the send method with its corresponding annotation. The lemma function that performs the updating of the ChannelState is defined at lines 42 to 64. The pre- and postcondition of this lemma function are defined at lines 31 to 40. We bundle all required heap predicates into the precondition on line 21, and split them from the postcondition in line 24. With a special ghost statement we prepare for the atomic action, and the next ghost statement executes the atomic action, see line 22 and 23. On line 47 we use the sep predicate to separate the ChannelState in the invariant, and on 62 its inverse. Between those two lines we have the full permission of the ChannelState, which we can open, to get the complete permission of itemsGhost. At this point we can do the update if needed, see lines 49 to 60.

Listing 25: Channel send annotation

1 public final class Channel { 2 //...

3 4 public boolean send ( String msg ) 5 /∗@ requires... ensures...@ ∗/ 6 { 7 //@ open[fc]Channel(); 8 //@ sema. makeHandle(); 9 sema . acquire (); 10 //@ open channel sema inv(this)(); 11 //@ assert itemList |−> ?l& ∗&l.List(?items); 12 //@ assert [1/2] queueMaxSize |−> ?qms;

13 14 boolean result ; 15 i f ( itemList . size () < queueMaxSize ) { 16 itemList . add ( msg ); 17 result = true ; 18 } else { 19 result = f a l s e ; 20 } 21 //@ closeP send(); 22 //@ produce lemma function pointer chunk(my ghost send op): a t o m i c s p a c e g h o s t o p e r a t i o n(inv,P send,Q send)() { c a l l(); } ; 23 //@ perform a t o m i c s p a c e g h o s t o p e r a t i o n(); 24 //@ openQ send(); 25 //@ close channel sema inv(this)(); 26 sema . release (); 27 //@ close[fc]Channel();

10The code is slightly rearranged for readability, in the appendix A.1 the original code is presented.

57 28 return result ; 29 } 30 /∗@ 31 p r e d i c a t eP send()= 32 i s c h a n n e l s e p(sep, inv, this, sepPred, unsepPred)& ∗& 33 i s c h a n n e l u n s e p(unsep, inv, this, sepPred, unsepPred)& ∗& 34 i s c h a n n e l s e n d(sendClientOp, inv, this, unsepPred, msg, pre, post)& ∗& 35 sepPred()& ∗& pre()& ∗& 36 [ 1 / 2 ]itemsGhost |−> items; 37 p r e d i c a t eQ send()= 38 [ 1 / 2 ]itemsGhost |−> (length(items) < qms? append(items, cons(msg, nil)): items)& ∗& 39 sepPred()& ∗& 40 post(length(items) < qms);

41

42 lemma void my ghost send op() 43 r e q u i r e sP send()& ∗& inv(); 44 e n s u r e sQ send()& ∗& inv(); 45 { 46 openP send(); 47 sep(); 48 open ChannelState( , ); 49 i f(length(items) < qms) 50 { 51 itemsGhost= append(items, cons(msg, nil)); 52 c l o s e ChannelState(append(items, cons(msg, nil)), ); 53 sendClientOp(true); 54 } 55 e l s e 56 { 57 itemsGhost= items; 58 c l o s e ChannelState(items, ); 59 sendClientOp(false); 60 }

61

62 unsep(); 63 c l o s eQ send(); 64 } 65 @∗/ 66 }

The quite similar treatment of the receive method is presented in appendix ??. The channel class is verifiable against its specifications. Therefore there are no race conditions and it is deadlock free. Also proven are the correctness of functional properties like the message sending and receiving and the limited queue size.

7.4 Client program

Possessing a specification for the channel which should be usable by clients, a specification of those clients is given. An important issue is the context requirements that were assumed in the verification of the channel have to be satisfied by the clients, i.e., have to be put in the specification of the clients. They where formed by functions with a pre and postcondition, the implementation of those functions should be provided in this verification. In this verification the specification uses the channel specification given in the previous section. VeriFast only uses the function type declarations, the two channel predicates and the methods

58 contracts during the verification of the channel. In the first version of the clients, there will be one sender and one receiver with a channel between them. The sender will send k messages though the channel, and the receiver will receive k messages. The sender and receiver both will do the execution of sending and receiving respectable in their own thread. We only present the sender side here, the receiver will go in an analogue manner. The complete code for the verification is presented in appendix A.3.

7.4.1 Client specification

A number of properties should hold for the program, which follow from the goal of this iteration of the verification. The properties are: 1. Deadlock and race free. 2. One main program with a channel, one sender and one receiver, which work in their own thread. 3. After executions k messages are sent and k messages are received. 4. The program can not receive more messages than have been sent. 5. The number of messages in the communication channel is the number of sent messages minus the number of received messages. 6. No message loss, all sent messages will be received.

7.4.2 Client VeriFast specification

First the Java code is developed after which the general specifications of the program are takent and transformed into a form that VeriFast can handle. In this section only the sender party is presented; the receiver will operate in a similar manner. In listing 26 we present the main program class, and in listing 27 we present the sender party. Their implementation is straightforward.

Listing 26: Channel client: program

1 public class Program { 2 public static int messageMaxCount ;//k 3 4 public static void main ( String [] args ) 5 { 6 Channel c = new Channel (); 7 8 SenderThread sender = new SenderThread ( c ); 9 Thread threadS = new Thread ( sender ); 10 threadS . start (); 11 12 ReceiverThread receiver = new ReceiverThread ( c ); 13 Thread threadR = new Thread ( receiver ); 14 threadR . start (); 15 16 threadS . join (); 17 threadR . join (); 18 }

59 19 }

Listing 27: Channel client: SenderThread class

1 class SenderThread implements Runnable { 2 Channel c ; 3 SenderThread ( Channel c ) 4 { 5 this . c = c ; 6 }

7 8 public void run () 9 { 10 String m ; 11 for ( int i = 0 ; i != Program . messageMaxCount ; i++) 12 { 13 for (;;) 14 { 15 boolean success = this . c . send ( m ); 16 i f ( success ) break ; 17 } 18 } 19 } 20 }

We can now transform the general specification of the program into a form that VeriFast can handle. We do so by considering how the list of properties is reflected in the formalization. 1. Deadlock and race free. For race condition freedom we do not have to make any specification; in this program there is no shared variable to which multiple threads need to write. In the channel class there is such a variable, but the previous verification verified the use of it. Because there are no locks and semaphores in the client code, there can be no deadlocks. 2. One main program with a channel and one sender and one receiver, which work in their own thread. This specification holds because of the structure of the program. The main program creates a channel class and two threads that start the execution of the senderThread and the receiverThread class. The senderThread class has to implement the Runnable interface and implement a method run to be executable by a newly created thread. 3. After executions k messages are sent and k messages are received. To prove this, we introduce two new ghost variables for counting the number of mes- sages (lines 2 and 3 of listing 28). In the precondition of the main method we bound the messageMaxCount variable to the specification variable mmc, and have an additional re- quirement that they are at least 1: Program messageMaxCount(?mmc) &*& 0 < mmc. In the post we bound the sendCount and receiveCount respectively to sc and rc, and check if they are equal to mmc: [ ]Program sendCount(?sc) &*& [ ]Program receiveCount(?rc) &*& mmc == sc &*& mmc == rc. Now it is up to the main method to achieve the postcondition. Shown in the annotation. Because we know the sending is not done in the main method, but in a separate thread, the senderThread class. We have to specify the postcondition of the class. We do this by specifying two predicates in the SenderThread class: a pre for the condition before starting the thread, and a post for the condition after the thread has executed, see lines 4 and 5 of listing 29. The constructor method has to end in a state that complies to the precondition

60 of the thread, so pre. 4. The program can not receive more messages than the program has sent. This is required to hold during the execution, thus this needs to be incorporated into the client invariant, see lines 6, 7 and 11 of listing 30. Fractions are used here because the other halves are used to specify the functionality in the pre and post predicates of the SenderThread. This invariant also enforce that the send count is from 0 up to and including the messagesMaxCount, see lines 6 to 10 of listing 30. 5. The number of messages in the communication channel is the number of sent messages minus the number of received messages. This requirement is slightly more difficult; it tells something about the content of the channel. Luckily we have foreseen this11, and we may use it in a client invariant because we have already taken measures to be able to separate this predicate from the rest of the invariant. This is needed for the send operation in the channel. We can add the ChannelState predicate to the client invariant, and use this predicate to reason about the state of the channel. This can be seen on lines 5 and 11 of listing 30. Line 5 states that there are no empty items in the itemList. 6. No message loss, all sent messages will be received. This last requirement is achieved because the number of messages sent will equal the number received, and if all messages are received, this is as much as are sent, and we can conclude that no messages were lost. A few more specification lines of listing 30 must be explained. Section 7.3 stated the need for a predicate to hold the residue of the separation of the ChannelState from the client invariant. This residue predicate, called my unsep pred, is seen on lines 16 to 24, and is exactly the same as client inv except for the ChannelState. A second requirement derived from the channel specification is the implementation of the separating lemma function and its inverse. Those lemma functions were defined in the channel verification, and where assumed during the verification of channel. To be able to prove the correctness of those assumption, it is necessary to provide the body of the functions, with their contract. VeriFast will verify the body against the function contract. If VeriFast is able to verify this, it means that the assumption we did in the channel verification is correct. The function for the separating and its inverse are given on line 26 to 42.

Listing 28: Channel client: program specification

1 public class Program { 2 //@ static int sendCount; 3 //@ static int receiveCount; 4 public static int messageMaxCount ;//k 5 6 public static void main ( String [] args ) 7 //@ requires class i n i t t o k e n(Program.class)& ∗& Program messageMaxCount(? mmc)& ∗&0 < mmc; 8 //@ ensures Program messageMaxCount(mmc)& ∗&[ ]Program sendCount(?sc)& ∗& [ ]Program receiveCount(?rc)& ∗& mmc == sc& ∗& mmc == rc; 9 { 10 //... 11 } 12 }

11With the help of Bart Jacobs.

61 Listing 29: Channel client: SenderThread class specification

1 class SenderThread implements Runnable { 2 Channel c ; 3 4 //@ predicate pre()= this.c |−> ?c& ∗&[ ]c.Channel()& ∗& [1/2] atomic space(client i n v(c))& ∗& [1/2]Program sendCount(0)& ∗& [1/3] Program messageMaxCount(?mmc)& ∗&0 <= mmc; 5 //@ predicate post()= this.c |−> ?c& ∗&[ ]c.Channel()& ∗& [1/2] atomic space(client i n v(c))& ∗& [1/3]Program messageMaxCount(?mmc)& ∗& [ 1 / 2 ]Program sendCount(?sc)& ∗& mmc == sc; 6 7 SenderThread ( Channel c ) 8 //@ requires[ ]c.Channel()& ∗& [1/2]atomic space(client i n v(c))& ∗& [1/2] Program sendCount(0)& ∗& [1/3]Program messageMaxCount(?mmc)& ∗&0 <= mmc; 9 //@ ensures pre(); 10 { 11 //... 12 }

13 14 public void run () 15 //@ requires pre(); 16 //@ ensures post(); 17 { 18 //... 19 } 20 }

Listing 30: Channel client: client invariant and lemma functions

1 /∗@ 2 f i x p o i n t boolean non n u l l(Objecto) { returno != null; }

3

4 p r e d i c a t e c t o r client i n v(Channelc)()= 5 c. ChannelState(?items,?qms)& ∗& forall(items, non n u l l) == true& ∗& 6 [ 1 / 2 ]Program sendCount(?sc)& ∗& 7 [ 1 / 2 ]Program receiveCount(?rc)& ∗& 8 [ 1 / 3 ]Program messageMaxCount(?mmc)& ∗& 9 0 <= sc& ∗& sc <= mmc& ∗& 10 0 <= rc& ∗& rc <= mmc& ∗& 11 length(items) == sc − rc& ∗& 12 rc <= sc;

13

14 p r e d i c a t e my sep pred()= true;

15

16 p r e d i c a t e c t o r my unsep pred(Channelc)(list items, int qms)= 17 f o r a l l(items, non n u l l) == true& ∗& 18 [ 1 / 2 ]Program sendCount(?sc)& ∗& 19 [ 1 / 2 ]Program receiveCount(?rc)& ∗& 20 [ 1 / 3 ]Program messageMaxCount(?mmc)& ∗& 21 0 <= sc& ∗& sc <= mmc& ∗& 22 0 <= rc& ∗& rc <= mmc& ∗& 23 length(items) == sc − rc& ∗& 24 rc <= sc;

25

26 lemma void my sep() 27 r e q u i r e s exists (?c)& ∗& my sep pred()& ∗& client i n v(c)();

62 28 e n s u r e sc. ChannelState(?items,?qms)& ∗& my unsep pred(c)(items, qms); 29 { 30 open client i n v(c)(); 31 a s s e r tc. ChannelState(?items,?qms); 32 c l o s e my unsep pred(c)(items, qms); 33 }

34

35 lemma void my unsep() 36 r e q u i r e s exists (?c)& ∗& my unsep pred(c)(?items,?qms)& ∗&c. ChannelState(items, qms); 37 e n s u r e s my sep pred()& ∗& client i n v(c)(); 38 { 39 open my unsep pred(c)( , ); 40 c l o s e client i n v(c)(); 41 c l o s e my sep pred(); 42 } 43 @∗/

7.4.3 Client VeriFast annotation

For VeriFast to be able to conclude that the postcondition is reachable from its precondition it requires additional annotation of the code. It is important to note: the source code for threads work as is for Java, but VeriFast can not work with it as it is. The source code for thread starting and joining needs changes. In the specifications of the thread class of VeriFast the join method is not even defined. We will use an additional class, the JoinableRunnable class, for the thread starting and joining. This class can join threads and it has the capability to catch exceptions in threads, when an exception occurs in a thread it is stored in a field of the JoinableRunnable class. In our verification this last capability is not used. The JoinableRunnable class is supplied with VeriFast in the example folder. 12 The source code changes are small, therefore the changes to the code are given together with the annotations, see listing 31.

Listing 31: Channel client: program annotation

1 public class Program { 2 //@ static int sendCount; 3 //@ static int receiveCount; 4 public static int messageMaxCount ;//k 5 6 public static void main ( String [] args ) 7 //@ requires class i n i t t o k e n(Program.class)& ∗& Program messageMaxCount(? mmc)& ∗&0 < mmc; 8 //@ ensures Program messageMaxCount(mmc)& ∗&[ ]Program sendCount(?sc)& ∗& [ ]Program receiveCount(?rc)& ∗& mmc == sc& ∗& mmc == rc; 9 { 10 //@ init c l a s s(); 11 Channel c = new Channel (); 12 //@ close client i n v(c)(); 13 //@ create a t o m i c s p a c e(client i n v(c));

14 15 SenderThread sender = new SenderThread ( c );

12It is possible to change the standard libraries of VeriFast to support thread starting and joining like the way we did. We decided to work with the supplied work around of VeriFast, because in this way no changes to VeriFast are needed to verify the code.

63 16 JoinableRunnable senderJoinable = ThreadingHelper . createJoinableRunnable ( sender ); 17 //@ close sender.pre(); 18 //@ senderJoinable.closeIt(); 19 Thread threadS = new Thread ( senderJoinable ); 20 threadS . start (); 21 22 ReceiverThread receiver = new ReceiverThread ( c ); 23 JoinableRunnable receiverJoinable = ThreadingHelper . createJoinableRunnable ( receiver ); 24 //@ close receiver.pre(); 25 //@ receiverJoinable.closeIt(); 26 Thread threadR = new Thread ( receiverJoinable ); 27 threadR . start (); 28 29 ThreadingHelper . join ( threadS , senderJoinable ); 30 //@ open sender.post(); 31 ThreadingHelper . join ( threadR , receiverJoinable ); 32 //@ open receiver.post(); 33 } 34 }

The SenderThread class contains two loops. In VeriFast each loop requires a loop invariant. In which all required heap chunks need to be specified (lines 11 and 14 of listing 32). The inner loop body has additional annotations: the first two ghost statements create special function pointers, see line 16 and 17 of listing 32. We have defined their type declaration in the channel verification, listing 22, and the implementation in listing 30. The ghost statement on line 19 in listing 32 also creates a function pointer predicate; the send was also declared in the channel verification, listing 23, but its implementation is on lines 33 to 43. The pre and postcondition of this atomic send action are specified as two predicates, P clientSend and Q clientSend, see lines 30 and 31. The my clientSend lemma function provides the stated effects of the send operation on the client state, in this case it is the increment of the sendCounter. With this function an even more important property is proven, the send lemma function that was assumed in the channel verification. For the same reason as the separating function, if VeriFast is able to prove this lemma function, then the assumption made what this function could accomplish is proven. The same holds for the not given receiver lemma function. The senderThread class is presented in listing 32; the receiver class is presented in the appendix A.3. For readability the code is rearranged, the last specification part (lines 29 to 44) originally belongs between lines 15 and 16.

Listing 32: Channel client: SenderThread class annotation

1 class SenderThread implements Runnable { 2 //...

3 4 public void run () 5 //@ requires pre(); 6 //@ ensures post(); 7 { 8 String m ; 9 int i ; 10 for ( i = 0 ; i != Program . messageMaxCount ; i++) 11 //@ invariant this.c |−> ?c& ∗&[ ]c.Channel()& ∗& [1/2] atomic space(client i n v(c))& ∗& [1/2]Program sendCount(i)& ∗& [ 1 / 3 ]Program messageMaxCount(?mmc)& ∗&i <= mmc;

64 12 { 13 for (;;) 14 //@ invariant this.c |−> c& ∗&[ ]c.Channel()& ∗& [1/2] atomic space(client i n v(c))& ∗& [1/2]Program sendCount(i) &∗& [1/3]Program messageMaxCount(mmc); 15 { 16 //@ produce lemma function pointer chunk(my sep): channel se p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e e x i s t s(c); call(); } ; 17 //@ produce lemma function pointer chunk(my unsep): channel unsep(client i n v(c),c, my sep pred, my unsep pred( c))() { c l o s e exists(c); call(); } ; 18 //@ close my sep pred(); 19 //@ produce lemma function pointer chunk(my clientSend): channel send(client i n v(c),c, my unsep pred(c),m, P clientSend,Q clientSend)(r) { c a l l(); } ; 20 //@ closeP c l i e n t S e n d(); 21 boolean success = this . c . send ( m ); 22 //@ openQ clientSend(success); 23 i f ( success ) break ; 24 } 25 } 26 //@ close post(); 27 } 28 } 29 /∗@ 30 p r e d i c a t eP c l i e n t S e n d()= [1/2]Program sendCount(i)& ∗& [1/3] Program messageMaxCount(mmc);//& ∗&i < mmc& ∗&0 <= mmc& ∗&0 <=i; 31 p r e d i c a t eQ clientSend(booleanr)= [1/2]Program sendCount(r?i+1:i)& ∗& [ 1 / 3 ]Program messageMaxCount(mmc);

32

33 lemma void my clientSend(booleanr) 34 r e q u i r e si < mmc& ∗&P c l i e n t S e n d()& ∗& my unsep pred(c)(?items,?qms); 35 e n s u r e sQ clientSend(r)& ∗& my unsep pred(c)(r? append(items, cons(m, nil) ): items, qms); 36 { 37 openP c l i e n t S e n d(); 38 open my unsep pred(c)(items, qms); 39 i f(r) Program. sendCount++; 40 c l o s eQ clientSend(r); 41 f o r a l l a p p e n d(items, cons(m, nil), non n u l l); 42 c l o s e my unsep pred(c)(r? append(items, cons(m, nil)): items, qms); 43 } 44 @∗/

The complete program with the specification and annotation is verifiable with VeriFast. Again this means no race conditions, no deadlocks and the code meets the specification. A second conclusion that can be made from this verification is the correctness of the verification which was given in 7.3. There four functions where assumed in the channel specification. The functions where used in that verification, what they accomplish is specified via a pre and post- condition. The correctness of those functions is verified in this verification by presenting their implementation and verifying the implementation against its pre and postcondition. There is a seemingly circular proof in the modular approach presented here. The proof in section 7.3 of the channel has made assumptions during the validation, the function declarations. Those assumptions are proven in this section, thus the channel verification depends on the client verifi- cation and the client verification depends on the channel verification. This kind of proofs seem to

65 have circular reasoning, which is harmful because it could result in proving unsound results. In our proof this is not the case: there is a bottom case which does not depend on any assumption, thereby allowing a well-founded proof. In our case this are the lemma function implementations, they can be verified without any assumptions. Stating it differently, they do not depend on the (verified) specification of the channel, the only requirement is that the pre and postconditions match. A similar kind of proof is explained in the paper [46], with a machine-checked proof for the correctness of this proof strategy. This argumentation holds also for the other iterations of the verification.

7.5 Client program with multiple senders and receivers

Having only one party on either side is a very limited the use of the channel. The channel should behave correctly in a setting with multiple parties on either side, this will be the next challenge in the verification. Note that we do not have to change the channel, only the client program. The requirement we want to verify is that if we have n senders and m receivers, and a sender sends k messages and a receiver receives l messages. For this program we will not have requirement that states something during the execution of threads, because this kind of requirements make the specification much harder. In the next section, this kind of requirements is added to the requirement of this section.

7.5.1 Client specification

A number of properties should hold for the program; they follow out of the goal of this iteration of the verification. The properties are: 1. Deadlock and race free. 2. One main program with a channel and n senders and m receivers, each working in their own thread. 3. After executions n ∗ k messages are sent and m ∗ l messages are received.

7.5.2 Client VeriFast specification

First we have to make the Java code after which we take general specifications of the program and transform it into a form that VeriFast can handle. In this section only the sender party is presented; the receiver is omitted, but could be presented in a similar manner. The complete code for the verification is presented in appendix A.4. In listing 33 we present the main program class, and in listing 34 we present the sender party. The idea for the code is that we have two arrays in the main program, one with the senders and one with the receivers. The SenderThread class has two properties for holding the Thread and JoinableRunnable objects. There is a slightly different design with respect to where the channel object is referenced. Previously this was also done in the SenderThread and ReceiverThread class, now there is public static program variable for this, allowing all classes to access it. This change makes the specification more understandable 13.

Listing 33: Channel client: program

1 public class Program { 2 public static Channel channel ;

13The same design as the previous client specification can be used, if one wants the Channel as a property of the SenderThread and ReceiverThread.

66 3 public static int sendersCount ;//n 4 public static int receiversCount ;//m 5 public static int sendMaxCount ;//k 6 public static int receiveMaxCount ;//l 7 8 public static void main ( String [] args ) 9 { 10 Program . channel = new Channel (); 11 12 SenderThread [] senders = new SenderThread [ Program . sendersCount ]; 13 for ( int i = 0 ; i < Program . sendersCount ; i++) 14 { 15 SenderThread s = new SenderThread (); 16 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( s ); 17 Thread t = new Thread ( j ); 18 s . thread = t ; 19 s . joinable = j ; 20 t . start (); 21 senders [ i ] = s ; 22 }

23

24 // Receiver thread creation, and thread starting.

25 26 for ( int j = 0 ; j < Program . sendersCount ; j++) 27 { 28 SenderThread sw = senders [ j ]; 29 ThreadingHelper . join ( sw . thread , sw . joinable ); 30 }

31

32 // Receiver thread joining 33 } 34 }

Listing 34: Channel client: SenderThread class

1 class SenderThread implements Runnable { 2 Thread thread ; 3 JoinableRunnable joinable ; 4 5 SenderThread ( Channel c ) 6 { 7 this . c = c ; 8 }

9 10 public void run () 11 { 12 String m ; 13 for ( int i = 0 ; i != Program . sendMaxCount ; i++) 14 { 15 for (;;) 16 { 17 boolean success = this . c . send ( m ); 18 i f ( success ) break ; 19 } 20 } 21 } 22 }

67 We can now transform the general specification of the program and translate it into a form that VeriFast can handle. Considering how the list of properties is reflected in the formalization. 1. Deadlock and race free. Holds for the same reasons as in section 7.4: no writes to shared variables and no locks. 2. One main program with a channel and n senders and m receivers, each working in their own thread. This specification is achieved by the structure of the program. 3. After executions n ∗ k messages are sent and m ∗ l messages are received. The first place we write this down in the code is as a postcondition of the main method, see line 17 of listing 35. Note that it is written in an abbreviated form. Also required is postcondition of the sender thread. Its postcondition requires it to have sent k messages. To be able to count the number of messages this sender has sent, a ghost variable is used, see line 2 of listing 36. We are now able to formulate the postcondition of this thread, see line 7 listing 36. The new specification for the program is given in listing 35.

Listing 35: Channel client: program specification

1 public class Program { 2 //@ static int sendCount; 3 //@ static int receiveCount; 4 public static Channel channel ; 5 public static int sendersCount ;//n 6 public static int receiversCount ;//m 7 public static int sendMaxCount ;//k 8 public static int receiveMaxCount ;//l 9 10 public static void main ( String [] args ) 11 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 12 Program channel(?channel)& ∗& 13 Program sendCount(?psc)& ∗& Program receiveCount(?prc)& ∗& psc==0& ∗& prc==0& ∗& 14 [ ]Program sendersCount(?sendersCount)& ∗&0 < sendersCount& ∗& 15 [ ]Program receiversCount(?receiversCount)& ∗&0 < receiversCount& ∗& 16 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& Program receiveMaxCount(? rmc)& ∗&0 < rmc;@ ∗/ 17 /∗@ ensures Program sendCount(smc ∗ sendersCount)& ∗& Program receiveCount( rmc ∗ receiversCount);@ ∗/ 18 { 19 //... 20 } 21 }

The specification of the SenderThread is given in listing 36.

Listing 36: Channel client: SenderThread class specification

1 public class SenderThread implements Runnable { 2 //@ int senderSendCount; 3 Thread thread ; 4 JoinableRunnable joinable ; 5 6 //@ predicate pre()=[ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ] atomic space(client i n v(c))& ∗&[ ]Program sendMaxCount(?smc)& ∗& this.

68 senderSendCount |−> ?ssc& ∗& ssc==0& ∗&0 < smc; 7 //@ predicate post()=[ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ] atomic space(client i n v(c))& ∗&[ ]Program sendMaxCount(?smc)& ∗& this. senderSendCount |−> ?ssc& ∗& ssc==smc; 8 9 public void run () 10 //@ requires pre(); 11 //@ ensures post(); 12 { 13 //... 14 } 15 }

Because we omit the requirements that apply during execution we are left with a small client invariant, see listing 37. The my sep and my unsep lemmas of listing 30 remain the same.

Listing 37: Channel client: client invariant

1 /∗@ 2 p r e d i c a t e c t o r client i n v(Channelc)()= 3 c. ChannelState(?items,?qms)& ∗& forall(items, non n u l l) == true; 4

5 p r e d i c a t e c t o r my unsep pred(Channelc)(list items, int qms)= 6 f o r a l l(items, non n u l l) == true; 7 @∗/

7.5.3 VeriFast client annotation

Having the specification of the methods given, we can continue with the annotations. Each loop in the main method requires a loop invariant. In the loop invariant we can state predicates about the contents of arrays. We can do this with an array slices, an array slice is a predicate that holds for a number of array items in an array. Beside the array slice we need to use array slice deep, with this predicate we keep a predicate per array element. To use this array slice deep we need to specify a new predicate. This predicate will hold the SenderThreads which are stored in the array. In listing 38 this additional predicate is given together with the annotated main method of the program class.

Listing 38: Channel client: program annotation

1 /∗@ 2 p r e d i c a t e senderWorker(unitu, SenderThreads; unit u2)= 3 s.thread |−> ?t& ∗&s.joinable |−> ?j& ∗& 4 t.Thread(j, true)& ∗&j. JoinableRunnable(s, true)& ∗&s.getClass() == SenderThread.class& ∗& 5 u2 == unit; 6 @∗/ 7 8 public class Program { 9 //...

10 11 public static void main ( String [] args ) 12 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 13 Program channel(?channel)& ∗& 14 Program sendCount(?psc)& ∗& Program receiveCount(?prc)& ∗& psc==0& ∗& prc ==0& ∗&

69 15 [ ]Program sendersCount(?sendersCount)& ∗&0 < sendersCount& ∗& 16 [ ]Program receiversCount(?receiversCount)& ∗&0 < receiversCount& ∗& 17 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& Program receiveMaxCount(?rmc) &∗&0 < rmc;@ ∗/ 18 /∗@ ensures Program sendCount(smc ∗ sendersCount)& ∗& Program receiveCount( rmc ∗ receiversCount);@ ∗/ 19 { 20 Program . channel = new Channel (); 21 //@ close client i n v(Program.channel)(); 22 //@ create a t o m i c s p a c e(client i n v(Program.channel));

23 24 SenderThread [] senders = new SenderThread [ Program . sendersCount ]; 25 //@ array s l i c e d e e p e m p t y c l o s e(senders, 0, senderWorker, unit); 26 for ( int i = 0 ; i < Program . sendersCount ; i++) 27 /∗@ invariant0 <=i& ∗&i <= sendersCount& ∗& 28 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space(client i n v( c))& ∗& 29 [ ]Program sendMaxCount(smc)& ∗&0 < smc& ∗& 30 [ ]Program sendersCount(sendersCount)& ∗&0 < sendersCount& ∗& 31 a r r a y s l i c e(senders,i, sendersCount,?elems)& ∗& all e q(elems, null) == true& ∗& 32 a r r a y s l i c e d e e p(senders, 0,i, senderWorker, unit, , ); 33 @∗/ 34 { 35 SenderThread s = new SenderThread (); 36 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( s ); 37 //@ closes.pre(); 38 //@j.closeIt(); 39 Thread t = new Thread ( j ); 40 s . thread = t ; 41 s . joinable = j ; 42 t . start (); 43 44 senders [ i ] = s ; 45 //@ array s l i c e s p l i t(senders,i,i+ 1); 46 //@ close senderWorker(unit, senders[i], unit); 47 //@ array s l i c e d e e p c l o s e(senders,i, senderWorker, unit); 48 }

49

50 // Receiver thread creation, and thread starting.

51 52 for ( int j = 0 ; j < Program . sendersCount ; j++) 53 /∗@ invariant0 <=j& ∗&j <= sendersCount& ∗& 54 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space(client i n v( c))& ∗& 55 [ ]Program sendersCount(sendersCount)& ∗&0 < sendersCount& ∗& 56 Program sendCount(j ∗ smc)& ∗&[ ]Program sendMaxCount(smc)& ∗& 57 a r r a y s l i c e d e e p(senders,j, sendersCount, senderWorker, unit, , ); 58 @∗/ 59 { 60 SenderThread sw = senders [ j ]; 61 ThreadingHelper . join ( sw . thread , sw . joinable ); 62 //@ open sw.post(); 63 //@ Program. sendCount += sw. senderSendCount; 64 } 65 //@ assert Program. sendCount == Program. sendMaxCount ∗ sendersCount; 66

67 // Receiver thread joining

70 68 } 69 }

In the invariant of the first loops we have to state two things, one that the array up to the index holds SenderThread elements and from the index to the end is empty. The second statement is easy specified (line 31 of listing 38). For the non-empty elements we use the array slice deep predicate, with this we can state that the elements are of a certain predicate (the senderWorker predicate). When the loop body is executed the loop invariant must be maintained. This means that the array slices need to be updated. The last element of the array slice needs to be split off because that element is just updated. Therefore not empty anymore. This is done at line 45. The array slice deep also needs to be updated because element i needs to be added. As can been seen in lines 46 and 47. The last two loops can do with less annotation. Because the arrays are not used after the loops, it is fine if we leak their information. Important in this loop is the update of the Program.sendCount with the send count of the SenderThread. This specification gives rise to a problem within VeriFast. VeriFast was unable to conclude the assertion of the sendCount and the multiplication of sendMaxCount and sendersCount. Because of the the interrenal definitions of multiplications in VeriFast. A suggested fix by Bart Jacobs was to help the SAT solver by additional multiplication lemmas. Regardless of the additional lemmas to help the SAT solver, they failed to produce the desired result; a second suggested fix was to use a different SAT solver, VeriFast comes with two SAT solvers. The Redux SAT solver did not have a problem with the multiplication, consequently the Redux SAT solver is used going forward. Having the main class annotated, the annotated version of the SenderThread class in listing 39 is presented next. We have seen how this works and no new concepts are used here, therefore the code and annotation is understandable without any explanations.

Listing 39: Channel client: SenderThread class annotation

1 public class SenderThread implements Runnable { 2 //...

3 4 public void run () 5 //@ requires pre(); 6 //@ ensures post(); 7 { 8 String m ; 9 for ( int i=0; i < Program . sendMaxCount ; i++) 10 //@ invariant[ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space (client i n v(c))& ∗& this. senderSendCount |−> ?ssc& ∗&[ ] Program sendMaxCount(?smc)& ∗&i == ssc& ∗&i <= smc; 11 { 12 for (;;) 13 //@ invariant[ ]Program channel(c)& ∗&[ ]c.Channel()& ∗&[ ] atomic space(client i n v(c))& ∗& this. senderSendCount |−> s s c& ∗&[ ] Program sendMaxCount(smc)& ∗&i < smc& ∗&i == ssc; 14 { 15 //@ produce lemma function pointer chunk(my sep): channel se p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c ); call(); } ; 16 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c ); call(); } ; 17 //@ close my sep pred();

71 18 //@ produce lemma function pointer chunk(my clientSend): channel send( c l i e n t i n v(c),c, my unsep pred(c),m,P clientSend,Q clientSend)( r) { c a l l(); } ; 19 //@ closeP c l i e n t S e n d(); 20 boolean success = Program . channel . send ( m ); 21 //@ openQ clientSend(success); 22 i f ( success ) break ; 23 } 24 } 25 //@ assert senderSendCount == Program. sendMaxCount; 26 //@ close post(); 27 } 28 /∗@ 29 p r e d i c a t eP c l i e n t S e n d()= this. senderSendCount |−> s s c& ∗&[ ] Program sendMaxCount(smc)& ∗& ssc < smc; 30 p r e d i c a t eQ clientSend(booleanr)= this. senderSendCount |−> ?ssc2& ∗& ssc2 ==(r? ssc+1: ssc)& ∗&[ ]Program sendMaxCount(smc); 31

32 lemma void my clientSend(booleanr) 33 r e q u i r e si < smc& ∗&P c l i e n t S e n d()& ∗& my unsep pred(c)(?items,?qms); 34 e n s u r e sQ clientSend(r)& ∗& my unsep pred(c)(r? append(items, cons(m, nil)) : items, qms); 35 { 36 openP c l i e n t S e n d(); 37 open my unsep pred(c)(items, qms); 38 i f(r) senderSendCount++;

39

40 f o r a l l a p p e n d(items, cons(m, nil), non n u l l); 41 c l o s e my unsep pred(c)(r? append(items, cons(m, nil)): items, qms); 42 c l o s eQ clientSend(r); 43 } 44 @∗/ 45 }

The complete code is verifiable with VeriFast, meaning there are no race conditions and no dead- locks in the program, and the program meets its specifications.

7.6 Client program with multiple senders and receivers and richer spec- ifications

The prior verification left out some desirable verification properties because adding them increased the complexity a lot for one iteration. In this section they are added in the verification again.

7.6.1 Client specification

We specify a number of properties that should hold for the program, they follow out of the goal of this iteration of the verification. The properties are: 1. Deadlock and race free. 2. One main program with a channel and one sender and one receiver, which work in their own thread. 3. After executions n ∗ k messages are sent and m ∗ l messages are received. 4. The program can not receive more messages than the program has sent.

72 5. The number of messages in the communication channel is the number of sent messages minus the number of received messages. 6. No message loss, all sent messages will be received.

7.6.2 Client VeriFast specification

The general specification of the program is now transformed and translated into a form that VeriFast can handle. This was accomplished by considering how the list of properties is reflected in the formalization. This section only presents the sender party; the receiver will go in a similar manner, see appendix A.5. 1. Deadlock and race free. Holds for the same reasons as in section 7.5; no writes to shared variables and no locks. 2. One main program with a channel and a m senders and n receivers, each working in their own thread. Holds for the same reasons as in section 7.5; the specification is achieved by the structure of the program. 3. After executions m ∗ k messages are sent and n ∗ l messages are received. Holds for the same reasons as in section 7.5. As done previous, the postcondition of the main method, see line 20 of 41. As we did before we need to specify a postcondition of the sender thread. Its postcondition need to be that it has sent k messages. The postcondition of this thread is formulate at lines 14 to 20 of listing 42. 4. The program can not receive more messages than the program has sent. To be able to conclude this, VeriFast needs to know the number of send and received messages at anytime during the execution. In the client program of section 7.4 ghost variables were used to hold those numbers, and they were maintained during the loop of the threads. The write permission to the variable is given to the thread. In section 7.5 each thread had its own counter, the thread maintained its counter, and after thread termination those counters where summed up. Each thread has write access to his own counter, and after completion it was returned from that thread to the main thread for the summation. With multiple senders and a client invariant, both strategies will not work. The counters need to be maintained by the threads during threads executions and also be readable by the client invariant for the summation of the send count or receive count. The count needs to be maintained in a place where the thread has write permission, but at other moments the client invariant has read permission to. We will do this via the sender thread array and array slice deep, in the array slice deep we use the value property to store in each individual array slice the count of the corresponding sender thread. The sender thread will need write permission to the corresponding single slice in the array, thus it will be able to update that variable. A consequence of this is that the thread needs to know its own position, or index, in the array. The arrays of the senders and receivers need to be at a place accessible by threads. Therefore we store them as public static variables of the main program. This is a minor change of the Java code. The VeriFast specification of this requirement is on lines 6 to 13 of listing 40. On line 6 the sender thread array is accessed. Line 8 states that the array elements are of a certain new predicate, senderWorkerInv defined at line 15 and 16. We also state that we have a list of send counts, ?lstSc. The receiver is similar, and this requirement is specified at line 13. A fixpoint lemma function sums the count lists, lines 18 to 23.

73 5. The number of messages in the communication channel is the number of sent messages minus the number of received messages. This can be easily expressed, because of the work required for the previous property. We need to check wether the number of messages in the buffer is the number of send messages (the sum of sender counts) minus the number of received messages (the sum of the sender counts). This is expressed at line 12 of listing 40. 6. No message loss, all sent messages will be received. This property is at this moment not proving in the verification. Only when we assume n ∗ k == m ∗ l in the precondition of the main, it is easily proven with VeriFast. For this we need to give a proof by hand. With the current standaard VeriFast Java libraries and the way we have made this verification this is not possible. We present the prove by hand first, then we will explain why this could not be proving with VeriFast. In the verification we have a client invariant which hold during and after the execution of the sender and receiver threads. By this we have three properties α :#received ≤ #send, β : #queue == #send − #received and γ : no empty items in the queue. We use those three properties in the prove for no message loss. For this we have to make a case distinction on n ∗ k and m ∗ l. • If n ∗ k ≤ m ∗ l. By the post condition of the sender thread we know that each sender has send k messages, having n senders means #send == n ∗ k. By the post condition of the receiver thread we know that each receiver has received l messages, having m receives means #received == m ∗ l. By α and substitution we have m ∗ l ≤ n ∗ k. By the case distinction we get n ∗ k == m ∗ l. Which means all send messages are received, and there can not be message loss. • If n∗k > m∗k. By the post condition of the sender thread we know that each sender has send k messages, having n senders means #send == n∗k. By the post condition of the receiver thread we know that each receiver has received l messages, having m receives means #received == m ∗ l. By β and substitution we have #queue == n ∗ k − m ∗ l. By the case distinction and β we know the queue has to hold at least one message. Having m ∗ l messages received from the n ∗ k send messages and that the remaining n ∗ k − m ∗ l messages are still in the queue, and by γ we are certain that there are no empty elements in the queue. We can conclude that if all send messages are received or still in the queue, then no message is lost (up to that moment). The issue is that after thread joining, we have multiple fractional heap chunks of the atomic space with the client invariant. The way we designed the verification, VeriFast can not conclude that it is only one atomic space with the same client invariant. Because of this VeriFast can not “open” the client invariant to use the properties stated in this invariant. Thus we can not use the properties in a reasoning like we did by hand. Modification to the VeriFast Java libraries or a different verification design could make the properties of the invariant accessible, but we have not tried to accomplish this. Next the client invariant is presented.

Listing 40: Channel client: client invariant

1 /∗@ 2 p r e d i c a t e c t o r client i n v(Channelc)()= 3 c. ChannelState(?items,?qms)& ∗& forall(items, non n u l l) == true& ∗& 4 [ 1 / 2 ]Program sendMaxCount(?smc)& ∗&0 < smc& ∗& 5 [ 1 / 2 ]Program receiveMaxCount(?rmc)& ∗&0 < rmc& ∗& 6 [ ]Program senders(?s)& ∗& 7 [ ]Program sendersCount(?sc)& ∗& 8 [ 1 / 3 ]array s l i c e d e e p(s, 0, sc, senderWorkerInv, unit, ,?lstSc)& ∗&

74 9 [ ]Program r e c e i v e r s(?r)& ∗& 10 [ ]Program receiversCount(?rc)& ∗& 11 [ 1 / 3 ]array s l i c e d e e p(r, 0, rc, receiverWorkerInv, unit, ,?lstRc)& ∗& 12 length(items) == sum(lstSc) − sum(lstRc)& ∗& 13 sum(lstRc) <= sum(lstSc);

14

15 p r e d i c a t e senderWorkerInv(unitu, SenderThread sw; int senderCount)= 16 [ 3 / 2 ]sw. senderSendCount |−> senderCount;

17

18 f i x p o i n t int sum(list vs) { 19 switch(vs) { 20 case nil: return 0; 21 case cons(h,t): returnh+ sum(t); 22 } 23 }

Note that the senderWorkerInv has one output parameter (the last parameter) which outputs the sender send count of that array element. An interesting detail that stands out in line 16 in listing 40, is the presences of a fractional permission greater than one. This is allowed (sound), but usage is tedious. This is sound because the predicate is only used in an array slice for which there is only a fraction. Getting a predicate out of the fractional array slice results in also having a fraction of that predicate. Making mistakes in the fractions can result in crashing VeriFast14. In this case it is correct and will not crash because the fraction is in fact a half, 3/2 ∗ 1/3, the 1/3 comes from the fraction of the array slice deep on line 8. The new program class, with sender threads is given as a static parameter is presented in listing 41. The SenderThread class with the new ghost variable myIndex, and in the pre and postcondition information about the sender thread array.

Listing 41: Channel client: program specification

1 public class Program { 2 //@ static int sendCount; 3 //@ static int receiveCount; 4 public static Channel channel ; 5 public static int sendersCount ;//n 6 public static int receiversCount ;//m 7 public static int sendMaxCount ;//k 8 public static int receiveMaxCount ;//l 9 public static SenderThread [] senders ; 10 public static ReceiverThread [] receivers ; 11 12 public static void main ( String [] args ) 13 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 14 Program channel(?channel)& ∗& 15 Program senders( )& ∗& Program r e c e i v e r s( )& ∗& 16 [ ]Program sendersCount(?sendersCount)& ∗&[ ]Program receiversCount(? receiversCount)& ∗& 17 0 < sendersCount& ∗&0 < receiversCount& ∗& 18 Program sendCount(?psc)& ∗& Program receiveCount(?prc)& ∗& psc==0& ∗& prc ==0& ∗& 19 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& Program receiveMaxCount(?rmc) &∗&0 < rmc;@ ∗/

14this is a bug in the current version.

75 20 /∗@ ensures Program sendCount(smc ∗ sendersCount)& ∗& Program receiveCount(rmc ∗ receiversCount);@ ∗/ 21 { 22 //... 23 } 24 }

Listing 42: Channel client: SenderThread class specification

1 public class SenderThread implements Runnable { 2 //@ int senderSendCount; 3 //@ int myIndex; 4 Thread thread ; 5 JoinableRunnable joinable ; 6 7 /∗@ predicate pre()= 8 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space(client i n v( c))& ∗& 9 [ ]Program sendMaxCount(?smc)& ∗&0 < smc& ∗& 10 [ ]Program senders(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 11 [ ]Program sendersCount(?sc)& ∗& myIndex < sc& ∗& 12 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, nil), cons(0, nil)); 13 @∗/ 14 /∗@ predicate post()= 15 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space(client i n v( c))& ∗& 16 [ ]Program sendMaxCount(?smc)& ∗& 17 [ ]Program senders(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 18 [ ]Program sendersCount(?sc)& ∗& myIndex < sc& ∗& 19 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, nil), cons(smc, nil)); 20 @∗/ 21 22 public void run () 23 //@ requires pre(); 24 //@ ensures post(); 25 { 26 //... 27 } 28 }

7.6.3 Client VeriFast annotation

For VeriFast to be able to conclude that the postcondition is reachable from its precondition it requires the annotation of the code. A second code change is required, namely in thread creation and starting. A problem is that when the thread is started, the client invariant needs to hold. In this client invariant all elements of the array are used to get the send count, while some of the elements are still empty. The creation and starting of the threads needs to separated. Because this is easier than changing the specification, but it requires a second source code change. In VeriFast there was insufficient support for fractional array slices. New lemmas where, with the help of Bart Jacobs, added to the VeriFast library to support this verification, those new lemma will be given in appendix A.6.

76 Because to no new concepts of VeriFast are used, we give the annotation without further descrip- tion.

Listing 43: Channel client: program annotation

1 public class Program { 2 //...

3 4 public static void main ( String [] args ) 5 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 6 Program channel(?channel)& ∗& 7 Program senders( )& ∗& Program r e c e i v e r s( )& ∗& 8 [ ]Program sendersCount(?sendersCount)& ∗&[ ]Program receiversCount(? receiversCount)& ∗& 9 0 < sendersCount& ∗&0 < receiversCount& ∗& 10 Program sendCount(?psc)& ∗& Program receiveCount(?prc)& ∗& psc==0& ∗& prc ==0& ∗& 11 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& Program receiveMaxCount(?rmc) &∗&0 < rmc;@ ∗/ 12 /∗@ ensures Program sendCount(smc ∗ sendersCount)& ∗& Program receiveCount(rmc ∗ receiversCount);@ ∗/ 13 { 14 Program . senders = new SenderThread [ Program . sendersCount ]; 15 //@ SenderThread[]s= Program.senders; 16 //@ leak Program senders(s); 17 //@ array s l i c e d e e p e m p t y c l o s e(s, 0, senderWorker, unit); 18 for ( int i = 0 ; i < Program . sendersCount ; i++) 19 /∗@ invariant0 <=i& ∗&i <= sendersCount& ∗& 20 [ ]Program senders(s)& ∗&[ ]Program sendersCount(sendersCount)& ∗& 21 [ 1 / 3 ]Program sendMaxCount(smc)& ∗&0 < smc& ∗& 22 a r r a y s l i c e(s,i, sendersCount,?elems)& ∗& all e q(elems, null) == true& ∗& 23 [ 1 / 3 ]array s l i c e d e e p(s, 0,i, senderWorkerInv, unit, ,?v)& ∗& a l l e q(v, 0) == true& ∗& 24 [ 2 / 3 ]array s l i c e d e e p(s, 0,i, senderWorkerNull, unit, , ); 25 @∗/ 26 { 27 Program . senders [ i ] = new SenderThread (); 28 //@ Program.senders[i]. myIndex=i; 29 //@ array s l i c e s p l i t(senders,i,i+ 1); 30 //@ close [2/3] senderWorkerNull(unit, senders[i], unit); 31 //@ close [1/3] senderWorkerInv(unit, senders[i], ); 32 //@ array s l i c e d e e p c l o s e p r e c i s e(2/3, senders,i, senderWorkerNull, unit); 33 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3, senders,i, senderWorkerInv, unit);

34

35 }

36

37 //...(Receivers: ReceiverThread creation and array filling)...

38 39 Program . channel = new Channel (); 40

41 //@ close client i n v(Program.channel)(); 42 //@ create a t o m i c s p a c e(client i n v(Program.channel)); 43 //@ close foreach i(0, nil, senderWorkerIndex); 44 for ( int i = 0 ; i < Program . sendersCount ; i++) 45 /∗@ invariant0 <=i& ∗&i <= sendersCount& ∗&

77 46 [ ]Program sendersCount(sendersCount)& ∗&[ ]Program senders(s)& ∗& 47 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space( c l i e n t i n v(c))& ∗&[ ]Program sendMaxCount(smc)& ∗&0 < smc& ∗& 48 [ 2 / 3 ]array s l i c e d e e p(s,i, sendersCount, senderWorkerNull, unit, , )& ∗& 49 [ 1 / 3 ]array s l i c e d e e p(s, 0,i, senderWorker, unit,?elements, ) &∗& foreach i(0, elements, senderWorkerIndex); 50 @∗/ 51 { 52 //@ array s l i c e d e e p s p l i t(s,i,i+ 1); 53 //@ array s l i c e d e e p o p e n p r e c i s e(2/3,s,i); 54 SenderThread sender = senders [ i ]; 55 //@ close [1/3] senderWorkerInv(unit, sender, ); 56 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3,s,i, senderWorkerInv, unit); 57 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( sender ); 58 //@ sender. myIndex=i; 59 //@ leak sender. myIndex |−> i; 60 //@ close sender.pre(); 61 //@j.closeIt(); 62 Thread t = new Thread ( j ); 63 t . start (); 64 65 sender . thread = t ; 66 sender . joinable = j ; 67 //@ close [1/3] senderWorker(unit, sender, unit); 68 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3, senders,i, senderWorker, unit );

69

70 //@ close foreach i(i+ 1, nil, senderWorkerIndex); 71 //@ close senderWorkerIndex(i, sender); 72 //@ close foreach i(i, cons(sender, nil), senderWorkerIndex); 73 //@ foreach i a p p e n d(0, elements, cons(sender, nil)); 74 }

75

76 //...(Receivers: ReceiverThread thread starting)...

77 78 for ( int j = 0 ; j < Program . sendersCount ; j++) 79 /∗@ invariant0 <=j& ∗&j <= sendersCount& ∗& 80 [ ]Program sendersCount(sendersCount)& ∗&[ ]Program senders(s)& ∗& 81 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space( c l i e n t i n v(c))& ∗& 82 Program sendCount(j ∗ smc)& ∗&[ ]Program sendMaxCount(smc)& ∗& 83 [ 1 / 3 ]array s l i c e d e e p(s,j, sendersCount, senderWorker, unit,? elements, )& ∗& foreach i(j, elements, senderWorkerIndex); 84 @∗/ 85 { 86 SenderThread sw = senders [ j ]; 87 ThreadingHelper . join ( sw . thread , sw . joinable ); 88 //@ open sw.post(); 89 //@ open foreach i(j, elements, senderWorkerIndex); 90 //@ open senderWorkerIndex(j, sw); 91 //@ int jj= sw. myIndex; 92 //@ assertj == jj; 93 //@ array s l i c e d e e p o p e n p r e c i s e(1/3,s,j); 94 //@ open senderWorkerInv(unit, sw, ); 95 //@ Program. sendCount += sw. senderSendCount; 96 } 97 //@ assert Program. sendCount == Program. sendMaxCount ∗ sendersCount;

78 98

99 //...(Receivers: ReceiverThread thread joining)... 100 } 101 } 102 /∗@ 103 p r e d i c a t e senderWorker(unitu, SenderThread SenderWorker; unit u2)= 104 [ 3 ] SenderWorker.thread |−> ?t& ∗& [3] SenderWorker.joinable |−> ?j& ∗& 105 [ 3 ]t.Thread(j, true)& ∗& [3]j. JoinableRunnable(SenderWorker, true)& ∗& SenderWorker.getClass() == SenderThread.class& ∗& 106 u2 == unit;

107

108 p r e d i c a t e senderWorkerNull(unitu, SenderThread SenderWorker; unit u2)= 109 [ 3 / 2 ] SenderWorker.thread |−> &∗& [3/2] SenderWorker.joinable |−> &∗& 110 [ 3 / 2 ] SenderWorker. myIndex |−> &∗& 111 SenderWorker.getClass() == SenderThread.class& ∗& [3/4] SenderWorker. senderSendCount |−> 0& ∗& 112 u2 == unit;

113

114 p r e d i c a t e senderWorkerIndex(inti, SenderThreadt)= 115 [ ]t. myIndex |−> i; 116 @∗/

Listing 44: Channel client: SenderThread class annotation

1 public class SenderThread implements Runnable { 2 //...

3 4 public void run () 5 //@ requires pre(); 6 //@ ensures post(); 7 { 8 String m ; 9 for ( int i=0; i < Program . sendMaxCount ; i++) 10 /∗@ invariant 11 [ ]Program channel(?c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space( c l i e n t i n v(c))& ∗& 12 [ ]Program sendMaxCount(?smc)& ∗&i <= smc& ∗& 13 [ ]Program senders(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 14 [ ]Program sendersCount(?sc)& ∗& myIndex < sc& ∗& 15 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, nil), cons(i, nil)); 16 @∗/ 17 { 18 for (;;) 19 /∗@ invariant 20 [ ]Program channel(c)& ∗&[ ]c.Channel()& ∗&[ ]atomic space( c l i e n t i n v(c))& ∗& 21 [ ]Program sendMaxCount(smc)& ∗&i < smc& ∗& 22 [ ]Program senders(s)& ∗&[ ]this. myIndex |−> myIndex& ∗& 23 [ ]Program sendersCount(sc)& ∗& myIndex < sc& ∗& 24 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, nil), cons(i, nil)); 25 @∗/ 26 { 27 //@ produce lemma function pointer chunk(my sep): channel se p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e e x i s t s(c); call(); } ;

79 28 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e e x i s t s(c); call(); } ; 29 //@ close my sep pred(); 30 //@ produce lemma function pointer chunk(my send): channel send( c l i e n t i n v(c),c, my unsep pred(c),m,P clientSend, Q clientSend)(r) { c a l l(); } ; 31 //@ closeP c l i e n t S e n d(); 32 boolean success = Program . channel . send ( m ); 33 //@ openQ clientSend(success); 34 i f ( success ) break ; 35 } 36 } 37 //@ close post(); 38 } 39 } 40 /∗@ 41 p r e d i c a t eP c l i e n t S e n d()= 42 [ ]Program sendMaxCount(smc)& ∗& 43 [ ]Program senders(s)& ∗&[ ]this. myIndex |−> myIndex& ∗& 44 [ ]Program sendersCount(sc)& ∗& myIndex < sc& ∗& 45 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons( t h i s, nil), cons(i, nil)); 46 p r e d i c a t eQ clientSend(booleanr)= 47 [ ]Program sendMaxCount(smc)& ∗& 48 [ ]Program senders(s)& ∗&[ ]this. myIndex |−> myIndex& ∗& 49 [ ]Program sendersCount(sc)& ∗& myIndex < sc& ∗& 50 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons( t h i s, nil),(r? cons(i+1, nil): cons(i, nil)));

51

52 lemma void my send(booleanr) 53 r e q u i r e si < smc& ∗&P c l i e n t S e n d()& ∗& my unsep pred(c)(?items,?qms); 54 e n s u r e sQ clientSend(r)& ∗& my unsep pred(c)(r? append(items, cons(m, nil)) : items, qms); 55 { 56 openP c l i e n t S e n d(); 57 open my unsep pred(c)(items, qms); 58 SenderThread[] senders= Program.senders; 59 i n t sendersCount= Program. sendersCount; 60 i n tk= myIndex; 61 a s s e r t [1/3]array s l i c e d e e p(senders, 0, sendersCount, senderWorkerInv, unit , ,?oldSenderCounts); 62 a r r a y s l i c e d e e p s p l i t(Program.senders, 0, myIndex); 63 a r r a y s l i c e d e e p s p l i t p r e c i s e(1/3, Program.senders, myIndex, Program. sendersCount, senderWorkerInv, unit, myIndex+ 1); 64 a r r a y s l i c e d e e p o p e n p r e c i s e(2/3, Program.senders, myIndex); 65 i n t myOldSendCount= senderSendCount; 66 i f(r) { 67 senderSendCount++; 68 } 69 i n t myNewSendCount= senderSendCount; 70 a r r a y s l i c e d e e p c l o s e(Program.senders, myIndex, senderWorkerInv, unit); 71 a r r a y s l i c e d e e p j o i n p r e c i s e(1/3, Program.senders, myIndex, myIndex+ 1, senderWorkerInv, unit, Program. sendersCount); 72 a r r a y s l i c e d e e p j o i n p r e c i s e(1/3, Program.senders, 0, myIndex, senderWorkerInv, unit, Program. sendersCount);

73

74 f o r a l l a p p e n d(items, cons(m, nil), non n u l l);

80 75 a s s e r t [1/3]array s l i c e d e e p(senders, 0, sendersCount, senderWorkerInv, unit , ,?newSenderCounts); 76 a s s e r t newSenderCounts == append(take(k, oldSenderCounts), cons( myNewSendCount, drop(1, drop(k, oldSenderCounts)))); 77 a s s e r t length(oldSenderCounts) == sendersCount; 78 a s s e r t0 <=1& ∗&0 <=k& ∗&1+k <= length(oldSenderCounts); 79 drop drop(1,k, oldSenderCounts); 80 a s s e r t newSenderCounts == append(take(k, oldSenderCounts), cons( myNewSendCount, drop(k+ 1, oldSenderCounts))); 81 sum take cons drop(k, oldSenderCounts, myNewSendCount); 82 a s s e r t sum(newSenderCounts) == sum(oldSenderCounts)+ myNewSendCount − myOldSendCount; 83 c l o s e my unsep pred(c)(r? append(items, cons(m, nil)): items, qms); 84 c l o s eQ clientSend(r); 85 } 86 @∗/

VeriFast is not able to update an item in the middle of the list, therefore we need lemmas to split the list, take the head of the second list, update the head, put the head back on top of the second list and merge the first list and the second list. This strategy including the updating of the send count is presented at lines 62 to 82 of listing 44. Because not all required functionality existed, some additional lemmas and predicates are required, they are presented in listing 45.

Listing 45: Channel client: some additional lemmas

1 /∗@ 2 lemma auto void sum a l l e q(list vs) 3 r e q u i r e s all e q(vs, 0) == true; 4 e n s u r e s sum(vs) == 0; 5 { 6 switch(vs) { 7 case nil: 8 case cons(x0, xs0): sum a l l e q(xs0); 9 } 10 }

11

12 lemma void sum take cons drop(intk, list xs, intx) 13 r e q u i r e s0 <=k& ∗&k < length(xs); 14 e n s u r e s sum(xs) == sum(append(take(k, xs), cons(x, drop(k+ 1, xs)))) − x+ head(take(1, drop(k, xs))); 15 { 16 switch(xs) { 17 case nil: 18 case cons(x0, xs0): 19 i f(k == 0) { 20 } e l s e { 21 sum take cons drop(k − 1 , xs0,x); 22 } 23 } 24 }

25

26 p r e d i c a t e foreach i (inti, list xs, predicate(int,t)p)= 27 switch(xs) { 28 case nil: return true; 29 case cons(x0, xs0): returnp(i, x0)& ∗& foreach i(i+ 1, xs0,p); 30 } ;

31

81 32 lemma void foreach i a p p e n d (inti, list xs, list ys) 33 r e q u i r e s foreach i(i, xs,?p)& ∗& foreach i(i+ length(xs), ys,p); 34 e n s u r e s foreach i(i, append(xs, ys),p); 35 { 36 switch(xs) { 37 case nil: 38 case cons(x0, xs0): 39 open foreach i(i, xs,p); 40 f o r e a c h i a p p e n d(i+ 1, xs0, ys); 41 c l o s e foreach i(i, append(xs, ys),p); 42 } 43 } 44 @∗/

The code is verifiable against the specification. At this point we know using the communication channel will not lead to any deadlocks, there are no race conditions, and the communication channel behaves as specified.

7.7 Remark on verification

We have to make a few small remarks on this verification. The verification is partial, we do not prove termination. If fact, we have proven a program with the possibility to never terminate. For example, if n ∗ k < m ∗ l the program will try to receive more messages then will be send, therefore at least one receiver thread will indefinitely wait until he has received l messages. The modular proving strategy works fine, but one has to be careful about the conclusions. For example, in the verification of the channel we have proven that the queue is limited. In the verification of the clients this detail is abstracted away from, only the notion that sending could not succeeds is used. This has as a result that proving a program with a limited queue and sending more messages then the queue could hold can be proven correctly. In our verification this is not a problem, but when termination needs to be proven this detail is crucial.

7.8 Source code improvements

During the verification process we made an observation about the quality and/or understandability of the programming code. 15 During the translation of the C# code to Java code we thought about its specifications. The code fragments we translated needed to have a clear specification, this appeared to be harder then expected. The code seemed to function correctly, as is claimed in the Capita Selecta work, but formulating the functionality of the code was difficult. The code is developed without the idea of correctly specifying each method, and this seemed to be a weak point. Especially methods that rely on synchronization constructs for correct behavior where hard to specify. The verification can be done on the original source code, but it was clear that changes to the code would give benefits, easier specifications, to the verification. For example, the original send method in the channel class had a locking strategy the worked but it was hard to specify, let even stand to verify. In listing 48 the original send method in C# code is presented 16. In listing 47 we show how this method is used in a method that represents the action of a transition. 15This may be outside the scope of verification, but due its importance and was made during the channel abstraction phase we present it here at the end of this chapter. 16A small alternation is done for readability, for example some comments and all logging commands are left out.

82 Listing 46: Original Send method

1 public bool Send ( SignalMessage msg , StateMachine sender , bool isFirstActionOfTransition ) 2 { 3 Monitor . Enter ( _lockObjPort );// acquire lock such that no other sm can use the channel at the same time 4 while ( _portAtoB . Count () >= queueLength )//while the queue is full we stay in this loop. 5 { 6 i f ( isFirstActionOfTransition )//if the buffer is full and the send is the first action ofa transition we need to return false. 7 { 8 Monitor . Exit ( _lockObjPort );// release the lock on the ports, such that other sm ' s can send. 9 return false ;//send can not be done because of full buffer, return f a l s e(the action is false and the transition is thereby not enabled) 10 } 11 else 12 { 13 _WaitForSendingGate . Reset ();// close the senders gate(queue is f u l l so all senders have to wait) 14 Monitor . Exit ( _lockObjPort );// release the lock on channel, let other sm ' s work on channel 15 sender . EndAtomicStep ();// end the atomic step of the sender ' s sm 16 _WaitForSendingGate . Wait ();// wait at this gate until it is opened again(...) 17 sender . BeginAtomicStep ();// new atomic step try again 18 i f ( sender . IsDestructing ) return false ;// abort the sending, the sm i s destructing 19 Monitor . Enter ( _lockObjPort );// get the lock again on channel 20 } 21 }

22 23 _portAtoB . Enqueue ( msg );// the SENDING;) 24 Monitor . Exit ( _lockObjPort );//exit the lock on the channel 25 26 i f ( _objectB != null ) _objectB . SignalStateMachines ();//signal all sm ' s on the receivers side 27 return true ; 28 }

Listing 47: Usages of original Send method

1 private bool ActionA2B ( object sender , smActionEventArgs args ) 2 { 3 bool result = true ; 4 5 result = Parent . Ports [”Q”]. Send (new SignalMessage (”msg1”, new object [] {}), this , true ); 6 i f (! result ) return false ; 7 EndAtomicStep (); 8 BeginAtomicStep (); 9 10 result = Parent . Ports [”Q”]. Send (new SignalMessage (”msg2”, new object [] {}), this , f a l s e );

83 11 return result ; 12 }

The main problem with specifying are the line 13 to 19 of listing 48, here we need to specify operations on synchronization constructs. The operations, e.g., locking and releasing of locks and semaphores, influence the execution of the current thread and another threads. We where not able to make a nice understandable specification for this. We made the observation that a redesign of the code would improve the the Send method. The synchronization statements should be there to make sure the execution behaves like it is specified, not there because we specify the locking17. In listing ?? we presented a new design of the Send method. In this design the synchronization constructs must be their to let the method execute like we expects it does. The method is not responsible anymore for retrying the send if it has to, that we have moved to the caller method, in listing 49 we see this. The redesign changed the way the state machine worked with the channel, but eventually it had the same result in functionality.

Listing 48: Redesigned Send method

1 public bool Send ( SignalMessage msg , StateMachine sender ) 2 { 3 Monitor . Enter ( _lockObjPort );// acquire lock such that no other sm can use the channel at the same time 4 i f ( _portAtoB . Count () >= queueLength )//IF the queue is full we... 5 { 6 _WaitForSendingGate . Reset ();// − c l o s e the senders gate(let all s e n d e r s wait) 7 Monitor . Exit ( _lockObjPort );// − e x i t the lock on the channel 8 return false ;// − and return false 9 }

10 11 _portAtoB . Enqueue ( msg );// the SENDING;) 12 Monitor . Exit ( _lockObjPort );// exit the lock on the channel 13 14 i f ( _objectB != null ) _objectB . SignalStateMachines ();//signal all sm ' s in the on the receivers side 15 return true ; 16 }

17 18 public void SenderWaitForRecieve ( StateMachine sender ) 19 { 20 _WaitForSendingGate . Wait ();// wait at this gate until it is opened 21 }

Listing 49: Usages of redesigned Send method

1 private bool ActionA2B ( object sender , smActionEventArgs args ) 2 { 3 bool result = true ; 4 5 result = Parent . Ports [”Q”]. Send (new SignalMessage (”msg1”, new object [] {}), this ); 6 i f (! result ) return false ; 7 EndAtomicStep ();

17We not claim this is always possible, but in this case it is.

84 8 BeginAtomicStep (); 9

10 while ( true ) 11 { 12 result = Parent . Ports [”Q”]. Send (new SignalMessage (”msg2”, new object [] { }), this ); 13 i f ( result | | IsDestructing ) break ;// if we have senda msg(or thread destructing) continue 14 EndAtomicStep ();// send the atomic step of the sender ' s sm 15 Parent . Ports [”Q”]. SenderWaitForRecieve ( this );// wait untila receiver has received something from the channel, then continue 16 BeginAtomicStep ();// we are again ina atomic step 17 }

18 19 return result ; 20 }

The verification process has led to an improvement of the source code, meanly to the under- standability of the code but this also influence the quality of the code in a positive sense. Also the effort for verification decreases because the specification that needs to be formulated are less complex.

85 8 Conclusions and future work

We have investigated the current state of formal verification tools for proving correctness of multi- threaded code, this is concluded in section 8.1. In section 8.2 we present the conclusions of the verification of a non-trivial essential part of the Toosm framework, the communication channel, which was preformed with VeriFast. This process gave insights into formal verification and verifi- cation with VeriFast in section 8.3. We address a number of threats to validity of this research in section 8.4. Finally we present some future work in section 8.5.

8.1 Verification techniques and tools

In this section, we present the conclusions of the research question A: What is the current state of art in tool-based verification of concurrent, object-oriented code? Verification of programming code is an actively researched area in computer science. There are a number of techniques aan tools available for verification of programs. In order to find a technique and tool, usable for verification of the communication channel, we list some preferences: (1) Since it is our goal to verify by means of tools, the technique needs to have tool support. (2) The technique and tool must support concurrency. (3) The technique and tool must be familiar with the object-oriented programming paradigm and (4) preferably operate on source code in C#. (5) Preferably, the tool and technique are relatively easy to understand and use and is actively maintained and supported. Based on these preferences, we looked into the following techniques: Hoare logic, Owicki-Gries logic, the Rely/Guarantee method, and Separation Logic. All techniques, besides Hoare logic are capable of describing concurrent programs. The techniques that support concurrency it is possible to use for the verification. Mainly due to the tool choice we use Separation logic. Separation logic is a technique that has been getting a lot of attention in the last couple of years. Separation logic is focussed around the heap and recourses. This technique is very well suited for verifying programming languages which supports pointers and a number of verification tools implement this technique. Separation logic lends itself extremely well for verification of concurrent programs because of the notion of local reasoning. Since we use C# which is pointer based with concurrency via threads, this technique looks promising for our verification. Based on the preferences stated above, we investigated the following tools: Spec#, Code Contracts, Dafny, Chalice, VeriFast, and ProPar. We investigated the tools by working through the available tutorials and other documentation and by implementing and verifying the simple summation example in multiple tools. Spec# is not maintained anymore and the lessons learned were implemented in Code Contracts. Unfortunately, concurrency was left out of Code Contracts making both tools unsuitable for our verification. Also Dafny does not support concurrency well. Chalice lacks the object oriented paradigm and ProPar on the other hand used its own language and paradigm. VeriFast scored, see tabel 2, well on all but one of our preferences: VeriFast can work with Java but not with C#. Fortunately, because of the straightforward programming constructs used, the translation of C# to Java is not to difficult, making VeriFast the best tool for this project. Most tools support verification of programs with concurrency, but it is not always easy to do this verification. We also note that in the last Software Verification Competition [47], of the “Verified Software: Theories, Tools and Experiments” international conference, all the problems where non-concurrent. Most tools support only one or two languages. From tool development view this choose is understandable, but if the language is fixed for a user it reduces the tools available to use drastically.

86 Table 2: Tool comparison

Tool Concurrency Object-Oriented Language Availability and ease of use SpecSharp 1 1 1 0 Code Contracts 0 1 1 1 Dafny 0 1 0 1/2 Chalice 1 1/2 1/2 0 VeriFast 1 1 1/2 1 ProPar 1 0 0 1/2

8.2 Verification of the communication channel

In this section, the conclusion of research question B is presented: Can one of the tools verify the correctness of concurrent object-oriented code, especially the generic code of Toosm? The goal was to investigate if and to what extend we could verify the generic code of Toosm using a verification tool. Based on the overview of techniques and tools, we chose to use VeriFast to do the verification of a generic part of Toosm, namely the communication channel. To make the verification feasible, we made abstractions to the channel and took an iterative approach, adding functionality with every iteration. To be precise, we abstracted away from the object that is transmitted, have no peek method for looking at the content of the channel and only looked at the communication channel and no other artifacts. The first iteration started with the verification of the channel in isolation. Here we proved race condition freedom, deadlock freedom, and a number of functional requirements, e.g., the functionality of the send method. In the next iteration the channel specifications where modified to be usable with clients. The verification properties remained the same. We extended the verification to a setting with one sender and one receiver and a parameterized number of messages sent and received. The verification showed that no race conditions and deadlocks where present in the code and that the code fulfilled a number of functional requirement, e.g., no more messages could have been received then where send. The next verification iteration verified a parameterized number of senders and receivers and messages sent and received. In this verification we only verified for no racing and no deadlocks. In the last verification we extended the verification properties with functional requirement, e.g., program can not receive more messages then have been send. In a total of 6 iterations, we managed to verify, with a tool, the correctness of the abstracted communication channel with regard to the requirements shown above. It important to remark that race condition freedom is easily achieved, because all that is required is a correct VeriFast specification. For achieving deadlock freedom, the developer has to make a lock order and specify via additional annotations the checks on the order. Because we did not need multiple synchronization constructs in the verification, deadlock freedom was also relatively easy to prove. Verification of purely functional requirements (without race conditions and deadlocks) is relatively straightforward. It is the combination of the race condition freedom and functional requirements that is hard. The combination of these properties causes the specification to becomes much more complex. For example see, in section 7.3, how we have proven that no racing is possible on the shared variable itemList of the channel and specify functionality of the send method on the list. Since we only needed a single synchronization construct, proving deadlock freedom in combination with race conditions and functional requirements did not add much complexity. When multiple synchronization constructs are used, we expect the verification to be even more difficult. In order to successfully specify and annotate the source code, some source code modifications were necessary. In some cases, synchronization constructs were moved or removed in the code, this

87 made the specification easier to write. An additional benefit was a beter understandability of the source code itself, see section 7.8.

8.3 Practicability and usefulness of tool-based approach

To conclude the last research question (question C): What lessons can be learned from this ap- proach?, we will first look back at the verification with VeriFast and later at tool-based verification in general. In this section we have also used our opinion which is based on the experience of this project.

8.3.1 VeriFast experience

The design of specifications and annotations for VeriFast heavily depend on the experience of the user. The entry level of simple non-concurrent specifications is low. It takes only a small amount of effort and time to master the basics of VeriFast, and therefore also Separation logic. For an example see the banking example which was presented in section 6.3. When the complexity of the requirements grow, the required experience with VeriFast also increases. Working with arrays and lists is not as easy as in programming languages. With limited experience, the step to concurrency can be with VeriFast, but when the developer combines concurrency with functional requirements, it becomes very complex and the necessary knowledge and experience required grows rapidly. The following verification concepts require in-depth knowledge and experience in using Veri- Fast: • Arrays and lists: the array slice predicate is not very complicated because the documen- tation on this subject is sufficiently clear. A more difficult concept for arrays, however, is the array slice deep predicate. Using this predicate is hard. The concept is not easy to understand and the use of this predicate is not well documented; • Thread starting/joining: In contrast to thread starting, which is rather simple, the joining of threads is rather hard. The VeriFast Java library does not support the join method of the thread class by default; • Atomic spaces and lemma function pointer chunks These concepts are difficult to understand for beginning users. They are explained in a number of scientific papers [44] [45], which are hard to read for non-scientific developers. Fortunately the use of those concepts becomes easier if one knows in which situations they can be used.

8.3.2 VeriFast effort

The required effort needed for verification naturally depends on the experience of the user. An expert on verification with VeriFast has significant less effort need to make the specification. Still, the effort needed for the verification is rather large. Writing verification specifications and annotations is not trivial and can therefore be a time consuming task. Also, the amount of specifications and annotations needed can become very large for complex cases. For a very simple program like the example in section 6.3 the VeriFast “overhead” is 125%. This means 25% more lines of annotation than lines of source code. Based on the investigation into different verification tools, this overhead seems to be the norm, rather than the exception. For the final verification in section 7, the amount of annotation needed is large, even though the program under verification is still rather small and simple with only basic functionality and specification. Regardless of the simple and basic functionality, there seems to be some kind of minimum amount of annotations required for formal verification. Larger and more complex systems will only increase

88 the annotation effort. The required annotation overhead needed for the verifications done in this thesis are presented in table 3. For the three client verification versions of chapter 7, only the channel18 and client files are taken into account.

Table 3: Code vs annotations comparison

Version LOC LOA Overhead Bank account example 20 25 125% First client version 72 245 340% Channel 35 145 414% Client 37 100 340% Second client version 93 286 308% Channel 35 145 414% Client 58 141 243% Third client version 98 536 547% Channel 35 145 414% Client 63 391 621%

Those findings seem to be confirmed by the the “Solution verbosity metrics” of [48] in which the verification results in a software verification competition are reported/compared against each other. We make the remark the many required annotation are rather simple, like the open and close annotation commands. Also the annotations for using atomic spaces is almost the same each time. In our case, the effort we spend on giving a formal verification of the communication channel is defendable because it is done on generic code that is used with every transformation. Some effort however will be needed to translate the results from the abstracted channel to the full Toosm communication channel.

8.3.3 VeriFast capabilities

VeriFast has some strong points which where for us important, namely: • Parameterized verification: Because VeriFast uses symbols instead of actual values for variables, it is capable of performing parameterized verification. This means that VeriFast can reason over not only one but for all possible values of a certain variable. This makes it possible to consider an unlimited number of threads communicating with the channel. We can also perform a verification with an unlimited memory use. This means we can verify programs with an infinite state space, which is very nice feature. • Concurrency via threads: VeriFast uses the Java concurrency model. This makes it very practicable to use in verification of Java like languages. Our C# code is very close to this. Not all synchronization constructs are implemented but this can be added if required. • Object-Oriented: Not all tools support the complete object oriented approach, but Veri- Fast does. • Fast verification results: VeriFast can, in most cases, perform a verification and present the results within a seconds. Other verification tools we found while answering research question A could take up to a few minutes to produce results, even with simple programs.

18 Channel consists of the files ‘Channel.javaspec’ and ‘Channel.java’.

89 • Symbolic debugging: The symbolic debug capability of the VeriFast IDE makes the verification process much developer friendly. We have seen verifications where VeriFast is used to prove safety requirements, like race conditions, in programs with concurrency, e.g., [49]. VeriFast is a good tool for these kind of verifications. We have also seen good results on functionality verifications on non-concurrent programs, see both Software Verification Competition enterings/participations of VSTTE 2010 [48] and VSTTE 2012 [47]. When this project started, no effort in verifications of functionality and safety requirements in programs with concurrency were known to us. During this project however, a verification of Linux’s USB BP keyboard driver was preformed[50], with unbounded number of threads, unbounded number of keyboards, and an unbounded amount of time the driver is running. How to make good specification in VeriFast depends on experience and on the desired verification properties. A good pointer is to use encapsulation of smaller predicates into larger predicates. This results in smaller method contracts and the ability to abstract away from data. This abstraction abstracts away the internals the original class which makes the verification more modular and understandable.

8.3.4 Formal verification experience

We see a common high knowledge and experience requirement for all the formal verification tools, which makes it difficult to use in the software development process. However, in some cases, formal verification can be beneficial. In those cases, formal verification will be a dedicated task for an experienced user. The benefits of formal verification can outweigh testing, but not in the majority of the cases. Formal verification tools are becoming more mature, and therefore their applicability and usability increase. However, this does not decrease the required knowledge and experience level of the user.

8.3.5 Verification approach

The iterative approach resulted in a successful verification. In the last iteration we managed to verify the goal set at the start of the project. The modular approach of first verifying the com- munication channel and then the client was successful, and coupling between the two verification was kept at a minimum. A consequence is that the verification has become more complex. Even though it is possible to verify source code that was not developed for formal verification, keeping formal verification in mind during the design and implementation of the software, makes the verification a lot easier. This is because the developer has to think carefully about the goals and responsibilities of the classes and methods. Especially in multi threaded code, where syn- chronization constructs are introduced, their purpose needs to be complectly clear in order to do formal verification. In separation logic this is even mandatory because synchronization constructs have invariants.

8.4 Threats to validity

In this project, we used a tool to do the verification. If this tool contains errors or faulty reasoning, it may have an impact on the results. In the case of VeriFast, we do not think this is the case because of the tool’s reputation, contest results [48][47] and the fact that the tool is still actively maintained. When a specification is incorrect, the results of VeriFast may be faulty. Because making a specification can become very complex this is a threat. Another threat to validity lies in two necessary translations: from hard-to-read specifications in SLCO to our own requirements and from that to VeriFast specifications for the communication

90 channel. Most requirements are self-evident and can be easily traced back to the specification in SLCO. However, because the translation is done manually and no proof is given, there are no guarantees that the SLCO specifications and the VeriFast specifications describe exactly the same behavior. But because of careful inspection we are highly confident that no errors or alternative behavior were introduced in the translations. We cannot claim that we have proven the implemented communication channel of Toosm since we made an abstraction for verification. Even so we believe that the general results are valid for the real implementation, some effort is needed to ensure the correctness of the implemented channel. A small issue is the programming languages used. Because VeriFast works with Java and Toosm works with C#, we had to translate the latter into the former. Because .NET and Java are different platforms, their implementation is different, especially the working of threads and synchronization constructs. During the translation we tried to find constructs with the same behavior in both languages and we are confident this was done successfully. A remark has to be made about the use of integer values as parameters for the verification. Having no relation between the number of send and received messages, n ∗ k and m ∗ l, it can be in the last iteration that n ∗ k < m ∗ l holds but, that we have proven m ∗ l messages are received. This clearly can not be the case in the real execution. During verification the client invariant still requires that #send ≤ #received. This fact is not incorporated into the postcondition m ∗ l messages are received. This weak point in the verification can be overcome by modeling the verification differently, but this also would require some modifications to the standard Java library of VeriFast.

8.5 Future work

This project leads to a number of possible project for the future. We describe the most promising ones. A fully validated Toosm framework is would be a nice project, but this is only worth if more use of Toosm is expected, otherwise the effort required for this is undefendable. A project like this may also require reinvestigating which tool is best suited for the task, because the complete code is significant larger and more complex. A project where the verification is extended for more complex client behavior, e.g., the state machines, is already very useful. This would also require that the peek method in the channel, which was abstracted away from, needs to be included in the new verification. The implementation of the state machines and the peek method results in having multiple synchronization constructs. Therefore, checking for deadlocks is required, making it an interesting project. In my opinion, VeriFast meets all the requirements for this kind of verification. With the techniques and tools evaluated and the lessons learned during this project, possible future work would be to reevaluate and re-implement Toosm. By revisiting the design, Toosm can be modified to be better verifiable. This may include a look at the design of SLCO. In addition, it is also possible to investigate the atomicity used in Toosm. One observation made in the Capita Selecta project was that using the atomicity of SLCO during transformation resulted in a very strict interleaving in the resulting code. We can ask whether a different atomicity would allow for better performance, i.e. more concurrent activities, yet still remain functionally correct with regard to the model in SLCO. A new project can use the results of the Capita Selecta and Masters project to investigate this question. This investigation into the the atomicity can be seen in a broader perspective. An investigation into the granularity of multi-threaded code generated in a MDE chain: coarse granularity vs fine granularity of code. In my opinion, there is a fine line between generating large blocks of code that operate in critical sections vs smaller atomicity almost on machine level instructions. The

91 first will properly have a performance penalty, but is easier to generate and formally verify, while the later will have a much bigger development time and much more complex verification, but it can have a much better performance on execution time. The best choose depends probably on the type of system developed, but other issues also play a part, i.e., reusability. A new MDE chain from SLCO to Java source code would be a nice project. A similar design of the code such as the design of the C# code is achievable with a small effort. If the project includes a redesign of the code, the project grows in complexity and effort.

92 References

[1] S. Roede. Creating threaded parallel executable code for models described by a domain specific language. Capita Selecta project, Computer Science and Engineering, Technische Universiteit Eindhoven, 2011. [2] M. van Amstel, M.G.J. van den Brand, and L. Engelen. An exercise in iterative domain- specific language design. In Proceedings of the Joint ERCIM Workshop on Software Evolution (EVOL) and International Workshop on Principles of Software Evolution (IWPSE), pages 48–57. ACM, 2010. [3] B. D. Theelen, O. Florescu, M. C. W. Geilen, J. Huang, P. H. A. van der Putten, and J. P. M. Voeten. Software/hardware engineering with the parallel object-oriented specification language. In Proceedings of the 5th IEEE/ACM International Conference on Formal Methods and Models for Codesign, MEMOCODE ’07, pages 139–148. IEEE Computer Society, 2007. [4] G.J. Holzmann. The SPIN model checker: Primer and reference manual. Addison-Wesley Professional, 2004. [5] Lego. Lego mindstorms website. mindstorms.lego.com/(visited april 2012), 2011. [6] Eclipse Foundation. Dafny: a language and program verifier for functional correctness. http: //www.eclipse.org/(visited may 2012), 2012. [7] S. Owicki and D. Gries. An axiomatic proof technique for parallel programs. Acta informatica, 6(4):319–340, 1976. [8] C.B. Jones. Development methods for computer programs including a notion of interference. PhD thesis, PhD thesis, Oxford University, June 1981. Printed as: Programming Research Group, Technical Monograph 25, 1981. [9] P. OHearn, J. Reynolds, and H. Yang. Local reasoning about programs that alter data structures. In Computer Science Logic, pages 1–19. Springer, 2001. [10] J.C. Reynolds. Separation logic: A logic for shared mutable data structures. In Logic in Computer Science, 2002. Proceedings. 17th Annual IEEE Symposium on, pages 55–74. IEEE, 2002. [11] E.W. Dijkstra. Guarded commands, nondeterminacy and formal derivation of programs. Communications of the ACM, 18(8):453–457, 1975. [12] S. Owicki and D. Gries. Verifying properties of parallel programs: An axiomatic approach. Communications of the ACM, 19(5):279–285, 1976. [13] L. Lamport. Proving the correctness of multiprocess programs. Software Engineering, IEEE Transactions on, (2):125–143, 1977. [14] E.W. Dijkstra. Programming Languages. Academic Press, 1968. [15] C.B. Jones. Specification and design of (parallel) programs. Information processing, 83(9):321, 1983. [16] C.B. Jones. Tentative steps toward a development method for interfering programs. ACM Transactions on Programming Languages and Systems (TOPLAS), 5(4):596–619, 1983. [17] P.W. O’hearn and D.J. Pym. The logic of bunched implications. Bulletin of Symbolic Logic, pages 215–244, 1999. [18] R.M. Burstall. Some techniques for proving correctness of programs which alter data struc- tures. Machine intelligence, 7(23-50):3, 1972.

93 [19] P.W. Ohearn. Resources, concurrency, and local reasoning. Theoretical Computer Science, 375(1):271–307, 2007. [20] S. Brookes. Variables as resource for shared-memory programs: Semantics and soundness. Electronic Notes in Theoretical Computer Science, 158:123–150, 2006. [21] Microsoft. Specsharp. http://research.microsoft.com/en-us/projects/ specsharp/(visited april 2012), 2012. [22] Principal Researcher in the Research in Software Engineering (RiSE) group at Microsoft Re- search. K. Rustan M. Leino. Personal communication at technische universiteit eindhoven (30-11-2011)., 2011. [23] Microsoft. Code contracts. http://research.microsoft.com/en-us/projects/ contracts/(visited april 2012), 2012. [24] Microsoft. Dafny: a language and program verifier for functional correctness. http: //research.microsoft.com/en-us/projects/dafny/(visited april 2012), 2012. [25] Microsoft. Chalice. http://research.microsoft.com/en-us/projects/chalice/(visited april 2012), 2012. [26] B. Jacobs and F. Piessens. The verifast program verifier. CW Reports, 2008.

[27] B. Jacobs. Verifast website. people.cs.kuleuven.be/~bart.jacobs/verifast/(visited april 2012), 2012. [28] A. Mooij and W. Wesselink. Incremental verification of owicki/gries proof outlines using pvs. Formal Methods and Software Engineering, pages 390–404, 2005. [29] W. Wesselink and A. Mooij. Propar, a tool for assertion-based verification of parallel pro- grams. http://www.win.tue.nl/~wieger/propar/(visited april 2012), 2012. [30] R. Bornat, C. Calcagno, and H. Yang. Variables as resource in separation logic. Electronic Notes in Theoretical Computer Science, 155:247–276, 2006. [31] S.S. Ishtiaq and P.W. O’hearn. Bi as an assertion language for mutable data structures. In ACM SIGPLAN Notices, volume 36, pages 14–26. ACM, 2001. [32] P.W. OHearn. A primer on separation logic (and automatic program verification and analysis). [33] V. Vafeiadis and M. Parkinson. A marriage of rely/guarantee and separation logic. CONCUR 2007–Concurrency Theory, pages 256–271, 2007. [34] X. Feng. Local rely-guarantee reasoning. In ACM SIGPLAN Notices, volume 44, pages 315–327. ACM, 2009. [35] T. Dinsdale-Young, L. Birkedal, P. Gardner, M. Parkinson, and H. Yang. Views: Composi- tional reasoning for concurrent programs. [36] N. Charlton, B. Horsfall, and B. Reus. Crowfoot: A verifier for higher-order store programs. In Verification, Model Checking, and Abstract Interpretation, pages 136–151. Springer, 2012. [37] A. Amighi, SCC Blom, M. Huisman, and M. Zaharieva-Stojanovski. The vercors project: Setting up basecamp. 2012. [38] G. Stewart, L. Beringer, and A.W. Appel. Verified heap theorem prover by paramodulation. [39] J. Berdine, B. Cook, , Ishtiaq, and S. Slayer: Memory safety for systems-level code. [40] C. Calcagno and D. Distefano. Infer: An automatic program verifier for memory safety of c programs. [41] B. Jacobs, J. Smans, and F. Piessens. The verifast program verifier: A tutorial.

94 [42] J. Smans, B. Jacobs, F. Piessens, W. Penninckx, F. Vogels, and P. Philippaerts. Verifying java programs with verifast. [43] B. Jacobs, J. Smans, P. Philippaerts, and F. Piessens. The verifast program verifier: A tutorial for java card developers. [44] B. Jacobs and F. Piessens. Modular full functional specification and verification of lock-free data structures. CW Reports, 2009. [45] B Jacobs, J. Smans, P. Philippaerts, F. Vogels, W. Penninckx, and F. Piessens. Verifast: A powerful, sound, predictable, fast verifier for c and java. In NASA Formal Methods, Lecture Notes in Computer Science. [46] B. Jacobs and F. Piessens. Expressive modular fine-grained concurrency specification. In Proceedings of the 38th annual ACM SIGPLAN-SIGACT symposium on Principles of pro- gramming languages, POPL ’11, pages 271–282, New York, NY, USA, 2011. ACM. [47] Tools Verified Software: Theories and Experiments. Vstte 2012 software verification compe- tition. https://sites.google.com/site/vstte2012/compet(visited May 2012), 2012. [48] V. Klebanov, P M¨uller,N. Shankar, G.T. Leavens, V. W¨ustholz,E. Alkassar, R. Arthan, D. Bronish, R. Chapman, C. Cohen, M. Hillebrand, B. Jacobs, K.R.M. Leino, R. Monahan, F. Piessens, N. Polikarpova, T. Ridge, J. Smans, S. Tobies, T Tuerk, M. Ulbrich, and Weiß. [49] C. Cuypers, B. Jacobs, and F. Piessens. Verification of data-race-freedom of a java chat server with verifast. CW reports, 2009. [50] W. Penninckx, J.T. M¨uhlberg, J. Smans, B. Jacobs, and F. Piessens. Sound formal verification of linux’s usb bp keyboard driver. NASA Formal Methods: 4th International Symposium, Nfm 2012, Norfolk, Va, USA, April 3-5, 2012, Proceedings, 7226, 2012.

95 A Appendix: Channel verification

All verification are done with version 12.2 of VeriFast. All verification require the REDUX SAT solver, i.e., “vfide.exe -prover redux client.jarsrc”.

A.1 Communication Channel (Ia)

The VeriFast files 19 required for the verification are: • Channels.jarsrc: implementation of the “Channels” module, this file contains information about which files are required for the channel source code. • Channel.java: source file belonging to the “Channel” module implementation, the file with the java code with the required annotation. • Channels.jarspec: specification of the “Channels” module, this file contains information about which files are required for the channel specification. • Channels.javaspec: source file belonging to the “Channels” module specification, this file contains the specification of the channel class, this file is used by the client for its verification. The content of the files are presented in the next four listings.

Listing 50: Channels.jarsrc

1 Channel . java

Listing 51: Channel.java

1 package channels ; 2 3 import java . util . concurrent . ∗ ; 4 import java . util . ∗ ; 5 6 /∗@ 7 8 p r e d i c a t e c t o r channel sema inv(Channel channel)()= 9 channel.itemList |−> ?itemList& ∗& itemList.List(?items)& ∗& [1/2]channel. itemsGhost |−> ?ghostItems& ∗& items == ghostItems& ∗& 10 [ 1 / 2 ]channel. queueMaxSize |−> ?qms& ∗& length(items) <= qms; 11 12 @∗/ 13 14 public final class Channel { 15 16 //@ list itemsGhost; 17 List itemList ; 18 Semaphore sema ; 19 int queueMaxSize ; 20 21 //@ predicate Channel()= sema |−> ?sema& ∗&[ ]sema. Semaphore(channel sema inv(this )); 22 //@ predicate ChannelState(list items, int qms)= [1/2] itemsGhost |−> items &∗& [1/2] queueMaxSize |−> qms; 23 24 public Channel () 25 //@ requires true; 26 //@ ensures Channel()& ∗& ChannelState(nil, ); 27 {

19A explantation on different file types and how they are used by VeriFast can be found in section 2 of [49]

96 28 itemList = new ArrayList (); 29 //queueMaxSize= 1; 30 //@ itemsGhost= nil; 31 //@ close channel sema inv(this)(); 32 //@ one time(channel sema inv(this)); 33 sema = new Semaphore ( 1 ) ; 34 //@ sema. leakHandle(); 35 //@ close Channel(); 36 //@ close ChannelState(nil, ); 37 } 38 39 boolean send ( String msg ) 40 /∗@ 41 r e q u i r e s 42 Channel()& ∗& 43 ChannelState(?items,?qms); 44 @∗/ 45 /∗@ 46 e n s u r e s 47 Channel()& ∗& 48 ChannelState( length(items) < qms? append(items, cons(msg, nil)): items, qms)& ∗& 49 r e s u l t ==(length(items) < qms); 50 @∗/ 51 { 52 //@ open Channel(); 53 //@ open ChannelState(items, qms); 54 //@ sema. makeHandle(); 55 sema . acquire (); 56 //@ open channel sema inv(this)(); 57 58 boolean result ; 59 i f ( itemList . size () < queueMaxSize ) { 60 itemList . add ( msg ); 61 //@ itemsGhost= append(items, cons(msg, nil)); 62 result = true ; 63 } else { 64 result = f a l s e ; 65 } 66 67 //@ close channel sema inv(this)(); 68 sema . release (); 69 //@ close ChannelState( , ); 70 //@ close Channel(); 71 return result ; 72 } 73 74 Object receive () 75 /∗@ 76 r e q u i r e s 77 Channel()& ∗& 78 ChannelState(?items,?qms); 79 @∗/ 80 /∗@ 81 e n s u r e s 82 Channel()& ∗& 83 ChannelState( tail(items), qms)& ∗& 84 r e s u l t ==(items != nil? head(items): null); 85 @∗/ 86 { 87 //@ open Channel(); 88 //@ open ChannelState(items, qms); 89 //@ sema. makeHandle(); 90 sema . acquire (); 91 //@ open channel sema inv(this)(); 92 String result ; 93 i f ( itemList . size ( ) == 0) {

97 94 result = null ; 95 } else { 96 result = ( String ) itemList . remove ( 0 ) ; 97 //@ itemsGhost= tail(itemsGhost); 98 } 99 100 //@ switch(items) { c a s e nil: case cons(i0, is0): } 101 //@ close channel sema inv(this)(); 102 sema . release (); 103 //@ close ChannelState( , ); 104 //@ close Channel(); 105 return result ; 106 } 107 108 }

Listing 52: Channels.jarspec

1 channels . javaspec

Listing 53: Channels.javaspec

1 package channels ; 2 3 public final class Channel { 4 5 //@ predicate Channel(); 6 //@ predicate ChannelState(list items, int qms); 7 8 public Channel (); 9 //@ requires true; 10 //@ ensures Channel()& ∗& ChannelState(nil, ); 11 12 boolean send ( String msg ); 13 /∗@ 14 r e q u i r e s 15 Channel()& ∗& 16 ChannelState(?items,?qms); 17 @∗/ 18 /∗@ 19 e n s u r e s 20 Channel()& ∗& 21 ChannelState( length(items) < qms? append(items, cons(msg, nil)): items, qms)& ∗& 22 r e s u l t ==(length(items) < qms); 23 @∗/ 24 25 Object receive (); 26 /∗@ 27 r e q u i r e s 28 Channel()& ∗& 29 ChannelState(?items,?qms); 30 @∗/ 31 /∗@ 32 e n s u r e s 33 Channel()& ∗& 34 ChannelState( tail(items), qms)& ∗& 35 r e s u l t ==(items != nil? head(items): null); 36 @∗/ 37 }

98 A.2 Communication Channel - client ready (Ib)

The VeriFast files required for the verification are: • Channels.jarsrc: implementation of the “Channels” module (presented in A.1). • Channel.java: source file belonging to the “Channel” module implementation. • Channels.jarspec: specification of the “Channels” module (presented in A.1). • Channels.javaspec: source file belonging to the “Channels” module specification. • verifast.jarspec: Specification of the “verifast” module, supplied with VeriFast in the example folder. • verifast.javaspec: source file belonging to the “verifast” module specification, supplied with VeriFast in the example folder. Because only “Channel.java” and “Channels.javaspec” are modified, only the content of those files are presented in the next two listings.

Listing 54: Channel.java

1 package channels ; 2 3 import java . util . concurrent . ∗ ; 4 import java . util . ∗ ; 5 6 /∗@ 7 p r e d i c a t e c t o r channel sema inv(Channel channel)()= 8 channel.itemList |−> ?itemList& ∗& itemList.List(?items)& ∗& [1/2]channel. itemsGhost |−> ?ghostItems& ∗& items == ghostItems& ∗& 9 [ 1 / 2 ]channel. queueMaxSize |−> ?qms& ∗& length(items) <= qms; 10 @∗/ 11 12 public final class Channel { 13 14 //@ list itemsGhost; 15 List itemList ; 16 Semaphore sema ; 17 int queueMaxSize ; 18 19 //@ predicate Channel()= sema |−> ?sema& ∗&[ ]sema. Semaphore(channel sema inv(this )); 20 //@ predicate ChannelState(list items, int qms)= [1/2] itemsGhost |−> items &∗& [1/2] queueMaxSize |−> qms; 21 22 public Channel () 23 //@ requires true; 24 //@ ensures Channel()& ∗& ChannelState(nil, ); 25 { 26 itemList = new ArrayList (); 27 //@ itemsGhost= nil; 28 //@ close channel sema inv(this)(); 29 //@ one time(channel sema inv(this)); 30 sema = new Semaphore ( 1 ) ; 31 //@ sema. leakHandle(); 32 //@ close Channel(); 33 //@ close ChannelState(nil, ); 34 } 35 36 boolean send ( String msg ) 37 /∗@ 38 r e q u i r e s 39 [?fc] Channel()& ∗& 40 [?fa]atomic space(?inv)& ∗&

99 41 i s c h a n n e l s e p(?sep, inv, this,?sepPred,?unsepPred)& ∗& 42 i s c h a n n e l u n s e p(?unsep, inv, this, sepPred, unsepPred)& ∗& 43 sepPred()& ∗& 44 i s c h a n n e l s e n d(?sendClientOp, inv, this, unsepPred, msg,?pre,?post)& ∗& pre(); 45 @∗/ 46 /∗@ 47 e n s u r e s 48 [fc] Channel()& ∗& 49 [fa]atomic space(inv)& ∗& 50 sepPred()& ∗& 51 post(result); 52 @∗/ 53 { 54 //@ open[fc] Channel(); 55 //@ sema. makeHandle(); 56 sema . acquire (); 57 //@ open channel sema inv(this)(); 58 //@ assert itemList |−> ?l& ∗&l.List(?items); 59 //@ assert [1/2] queueMaxSize |−> ?qms; 60 61 boolean result ; 62 i f ( itemList . size () < queueMaxSize ) { 63 itemList . add ( msg ); 64 result = true ; 65 } else { 66 result = f a l s e ; 67 } 68 69 { 70 /∗@ 71 p r e d i c a t eP send()= 72 i s c h a n n e l s e p(sep, inv, this, sepPred, unsepPred)& ∗& 73 i s c h a n n e l u n s e p(unsep, inv, this, sepPred, unsepPred)& ∗& 74 i s c h a n n e l s e n d(sendClientOp, inv, this, unsepPred, msg, pre, post)& ∗& 75 sepPred()& ∗& pre()& ∗& 76 [ 1 / 2 ] itemsGhost |−> items; 77 p r e d i c a t eQ send()= 78 [ 1 / 2 ] itemsGhost |−> (length(items) < qms? append(items, cons(msg, nil)) : items)& ∗& 79 sepPred()& ∗& 80 post(length(items) < qms); 81 82 lemma void my ghost send op() 83 r e q u i r e sP send()& ∗& inv(); 84 e n s u r e sQ send()& ∗& inv(); 85 { 86 openP send(); 87 sep(); 88 open ChannelState( , ); 89 i f(length(items) < qms) 90 { 91 itemsGhost= append(items, cons(msg, nil)); 92 c l o s e ChannelState(append(items, cons(msg, nil)), ); 93 sendClientOp(true); 94 } 95 e l s e 96 { 97 itemsGhost= items; 98 c l o s e ChannelState(items, ); 99 sendClientOp(false); 100 } 101 102 unsep(); 103 c l o s eQ send(); 104 } 105 @∗/

100 106 //@ closeP send(); 107 //@ produce lemma function pointer chunk(my ghost send op): a t o m i c s p a c e g h o s t o p e r a t i o n(inv,P send,Q send)() { c a l l(); } ; 108 //@ perform a t o m i c s p a c e g h o s t o p e r a t i o n(); 109 //@ openQ send(); 110 } 111 //@ close channel sema inv(this)(); 112 sema . release (); 113 //@ close[fc] Channel(); 114 return result ; 115 } 116 117 String receive () 118 /∗@ 119 r e q u i r e s 120 [?fc] Channel()& ∗& 121 [?fa]atomic space(?inv)& ∗& 122 i s c h a n n e l s e p(?sep, inv, this,?sepPred,?unsepPred)& ∗& 123 i s c h a n n e l u n s e p(?unsep, inv, this, sepPred, unsepPred)& ∗& 124 i s c h a n n e l r e c e i v e(?receiveClientOp, inv, this, unsepPred,?pre,?post)& ∗& 125 sepPred()& ∗& pre(); 126 @∗/ 127 /∗@ 128 e n s u r e s 129 [fc] Channel()& ∗& 130 [fa]atomic space(inv)& ∗& 131 sepPred()& ∗& 132 post(result); 133 @∗/ 134 { 135 //@ sema. makeHandle(); 136 sema . acquire (); 137 //@ open channel sema inv(this)(); 138 String result ; 139 i f ( itemList . size ( ) == 0) { 140 result = null ; 141 } else { 142 result = ( String ) itemList . remove ( 0 ) ; 143 } 144 //@ list items= itemsGhost; 145 { 146 /∗@ 147 p r e d i c a t eP()= 148 i s c h a n n e l s e p(sep, inv, this, sepPred, unsepPred)& ∗& 149 i s c h a n n e l u n s e p(unsep, inv, this, sepPred, unsepPred)& ∗& 150 i s c h a n n e l r e c e i v e(receiveClientOp, inv, this, unsepPred, pre, post)& ∗& 151 sepPred()& ∗& pre()& ∗& 152 [ 1 / 2 ] itemsGhost |−> items; 153 p r e d i c a t eQ()= 154 [ 1 / 2 ] itemsGhost |−> t a i l(items)& ∗& 155 sepPred()& ∗& 156 post(items != nil? head(items): null); 157 158 lemma void my ghost op() 159 r e q u i r e sP()& ∗& inv(); 160 e n s u r e sQ()& ∗& inv(); 161 { 162 openP(); 163 sep(); 164 open ChannelState( , ); 165 itemsGhost= tail(items); 166 c l o s e ChannelState(tail(items), ); 167 receiveClientOp(); 168 unsep(); 169 c l o s eQ(); 170 } 171 @∗/

101 172 //@ closeP(); 173 //@ produce lemma function pointer chunk(my ghost op): a t o m i c s p a c e g h o s t o p e r a t i o n(inv,P,Q)() { c a l l(); } ; 174 //@ perform a t o m i c s p a c e g h o s t o p e r a t i o n(); 175 //@ openQ(); 176 } 177 //@ switch(items) { c a s e nil: case cons(i0, is0): } 178 //@ close channel sema inv(this)(); 179 sema . release (); 180 return result ; 181 } 182 }

Listing 55: Channels.javaspec

1 package channels ; 2 3 /∗@ 4 5 t y p ed e f lemma void channel s e p(predicate() inv, Channelc, predicate() sepPred, p r e d i c a t e(list , int) unsepPred)(); 6 r e q u i r e s sepPred()& ∗& inv(); 7 e n s u r e sc. ChannelState(?items,?qms)& ∗& unsepPred(items, qms); 8 9 t y p ed e f lemma void channel unsep(predicate() inv, Channelc, predicate() sepPred, p r e d i c a t e(list , int) unsepPred)(); 10 r e q u i r e s unsepPred(?items,?qms)& ∗&c. ChannelState(items, qms); 11 e n s u r e s sepPred()& ∗& inv(); 12 13 t y p ed e f lemma void channel send(predicate() inv, Channelc, predicate(list , int) unsepPred, String msg, predicate() pre, predicate(boolean) post)(booleanr); 14 r e q u i r e s pre()& ∗& unsepPred(?items,?qms); 15 e n s u r e s post(r)& ∗& unsepPred(r? append(items, cons(msg, nil)): items, qms); 16 17 t y p ed e f lemma void channel r e c e i v e(predicate() inv, Channelc, predicate(list , i n t) unsepPred, predicate() pre, predicate(Object) post)(); 18 r e q u i r e s pre()& ∗& unsepPred(?items,?qms); 19 e n s u r e s post(items != nil? head(items): null)& ∗& unsepPred(tail(items), qms); 20 21 @∗/ 22 23 public final class Channel { 24 25 //@ predicate Channel(); 26 //@ predicate ChannelState(list items, int queueSizeMax); 27 28 public Channel (); 29 //@ requires true; 30 //@ ensures Channel()& ∗& ChannelState(nil, ); 31 32 public boolean send ( String msg ); 33 /∗@ 34 r e q u i r e s 35 [?fc] Channel()& ∗& 36 [?fa]atomic space(?inv)& ∗& 37 i s c h a n n e l s e p(?sep, inv, this,?sepPred,?unsepPred)& ∗& 38 i s c h a n n e l u n s e p(?unsep, inv, this, sepPred, unsepPred)& ∗& 39 i s c h a n n e l s e n d(?send, inv, this, unsepPred, msg,?pre,?post)& ∗& 40 sepPred()& ∗& pre(); 41 @∗/ 42 /∗@ 43 e n s u r e s 44 [fc] Channel()& ∗& 45 [fa]atomic space(inv)& ∗& 46 sepPred()& ∗& 47 post(result);

102 48 @∗/ 49 50 public String receive (); 51 /∗@ 52 r e q u i r e s 53 [?fc] Channel()& ∗& 54 [?fa]atomic space(?inv)& ∗& 55 i s c h a n n e l s e p(?sep, inv, this,?sepPred,?unsepPred)& ∗& 56 i s c h a n n e l u n s e p(?unsep, inv, this, sepPred, unsepPred)& ∗& 57 i s c h a n n e l r e c e i v e(?receive, inv, this, unsepPred,?pre,?post)& ∗& 58 sepPred()& ∗& pre(); 59 @∗/ 60 /∗@ 61 e n s u r e s 62 [fc] Channel()& ∗& 63 [fa]atomic space(inv)& ∗& 64 sepPred()& ∗& 65 post(result); 66 @∗/ 67 68 }

A.3 Client - version 1 (II)

The VeriFast files required for this verification are: • Client.java: source file belonging to the “Client” module implementation. • Client.jarsrc: implementation of the “Client” module. • Channels.jarspec: specification of the “Channels” module (presented in A.1). • Channels.javaspec: source file belonging to the “Channels” module specification (pre- sented in A.2). • verifast.jarspec: Specification of the “verifast” module, supplied with VeriFast in the example folder. • verifast.javaspec: source file belonging to the “verifast” module specification, supplied with VeriFast in the example folder. Only the content of the new files are presented in the next two listings.

Listing 56: Client.javasrc

1 verifast . jar 2 channels . jar 3 client . java

Listing 57: Channels.java

1 import channels . ∗ ; 2 import verifast . ∗ ; 3 4 /∗@ 5 6 f i x p o i n t boolean non n u l l(Objecto) { r e t u r no != null; } 7 8 p r e d i c a t e c t o r client i n v(Channelc)()= 9 c. ChannelState(?items,?qms)& ∗& forall(items, non n u l l) == true& ∗& 10 [ 1 / 2 ]Program sendCount(?sc)& ∗& 11 [ 1 / 2 ]Program receiveCount(?rc)& ∗&

103 12 [ 1 / 3 ]Program messageMaxCount(?mmc)& ∗& 13 0 <= sc& ∗& sc <= mmc& ∗& 14 0 <= rc& ∗& rc <= mmc& ∗& 15 l e n g t h(items) == sc − rc& ∗& 16 r c <= sc; 17 18 p r e d i c a t e my sep pred()= true; 19 20 p r e d i c a t e c t o r my unsep pred(Channelc)(list items, int qms)= 21 f o r a l l(items, non n u l l) == true& ∗& 22 [ 1 / 2 ]Program sendCount(?sc)& ∗& 23 [ 1 / 2 ]Program receiveCount(?rc)& ∗& 24 [ 1 / 3 ]Program messageMaxCount(?mmc)& ∗& 25 0 <= sc& ∗& sc <= mmc& ∗& 26 0 <= rc& ∗& rc <= mmc& ∗& 27 l e n g t h(items) == sc − rc& ∗& 28 r c <= sc;//& ∗& length(items) <= qms; 29 30 lemma void my sep() 31 r e q u i r e s exists (?c)& ∗& my sep pred()& ∗& client i n v(c)(); 32 e n s u r e sc. ChannelState(?items,?qms)& ∗& my unsep pred(c)(items, qms); 33 { 34 open client i n v(c)(); 35 a s s e r tc. ChannelState(?items,?qms); 36 c l o s e my unsep pred(c)(items, qms); 37 } 38 39 lemma void my unsep() 40 r e q u i r e s exists (?c)& ∗& my unsep pred(c)(?items,?qms)& ∗&c. ChannelState( items, qms); 41 e n s u r e s my sep pred()& ∗& client i n v(c)(); 42 { 43 open my unsep pred(c)( , ); 44 c l o s e client i n v(c)(); 45 c l o s e my sep pred(); 46 } 47 48 @∗/ 49 50 class SenderThread implements Runnable { 51 52 Channel c ; 53 54 //@ predicate pre()= this.c |−> ?c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/2]Program sendCount(0)& ∗& [1/3]Program messageMaxCount(? mmc)& ∗&0 <= mmc; 55 //@ predicate post()= this.c |−> ?c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/3]Program messageMaxCount(?mmc)& ∗& [1/2]Program sendCount (?sc)& ∗& mmc == sc; 56 57 SenderThread ( Channel c ) 58 //@ requires[ ]c. Channel()& ∗& [1/2]atomic space(client i n v(c))& ∗& [1/2] Program sendCount(0)& ∗& [1/3]Program messageMaxCount(?mmc)& ∗&0 <= mmc; 59 //@ ensures pre(); 60 { 61 this . c = c ; 62 } 63 64 public void run () 65 //@ requires pre(); 66 //@ ensures post(); 67 { 68 String m =”Hello”; 69 int i ; 70 for ( i = 0 ; i != Program . messageMaxCount ; i++) 71 //@ invariant this.c |−> ?c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/2]Program sendCount(i)& ∗& [1/3]

104 Program messageMaxCount(?mmc)& ∗&i <= mmc; 72 { 73 for (;;) 74 //@ invariant this.c |−> c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/2]Program sendCount(i)& ∗& [1/3] Program messageMaxCount(mmc); 75 { 76 77 /∗@ 78 p r e d i c a t eP c l i e n t S e n d()= [1/2]Program sendCount(i)& ∗& [1/3] Program messageMaxCount(mmc);//& ∗&i < mmc& ∗&0 <= mmc& ∗&0 <=i; 79 80 p r e d i c a t eQ clientSend(booleanr)= [1/2]Program sendCount(r?i+1:i )& ∗& [1/3]Program messageMaxCount(mmc); 81 82 lemma void my clientSend(booleanr) 83 r e q u i r e si < mmc& ∗&P c l i e n t S e n d()& ∗& my unsep pred(c)(?items,? qms); 84 e n s u r e sQ clientSend(r)& ∗& my unsep pred(c)(r? append(items, cons( m, nil)): items, qms); 85 { 86 openP c l i e n t S e n d(); 87 open my unsep pred(c)(items, qms); 88 i f(r) Program. sendCount++; 89 c l o s eQ clientSend(r); 90 f o r a l l a p p e n d(items, cons(m, nil), non n u l l); 91 c l o s e my unsep pred(c)(r? append(items, cons(m, nil)): items, qms) ; 92 } 93 @∗/ 94 //@ produce lemma function pointer chunk(my sep): channel s e p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 95 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 96 //@ close my sep pred(); 97 //@ produce lemma function pointer chunk(my clientSend): channel send( c l i e n t i n v(c),c, my unsep pred(c),m,P clientSend,Q clientSend)(r ) { c a l l(); } ; 98 //@ closeP c l i e n t S e n d(); 99 boolean success = this . c . send ( m ); 100 //@ openQ clientSend(success); 101 i f ( success ) break ; 102 } 103 } 104 //@ close post(); 105 } 106 107 } 108 109 class ReceiverThread implements Runnable { 110 111 Channel c ; 112 113 //@ predicate pre()= this.c |−> ?c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/2]Program receiveCount(0)& ∗& [1/3]Program messageMaxCount (?mmc)& ∗&0 <= mmc; 114 //@ predicate post()= this.c |−> ?c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/3]Program messageMaxCount(?mmc)& ∗& [1/2] Program receiveCount(mmc); 115 116 ReceiverThread ( Channel c ) 117 //@ requires[ ]c. Channel()& ∗& [1/2]atomic space(client i n v(c))& ∗& [1/2] Program receiveCount(0)& ∗& [1/3]Program messageMaxCount(?mmc)& ∗&0 <= mmc; 118 //@ ensures pre(); 119 {

105 120 this . c = c ; 121 } 122 123 public void run () 124 //@ requires pre(); 125 //@ ensures post(); 126 { 127 for ( int i = 0 ; i < Program . messageMaxCount ; i++) 128 //@ invariant this.c |−> ?c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/2]Program receiveCount(i)& ∗& [1/3] Program messageMaxCount(?mmc)& ∗&i <= mmc; 129 { 130 for (;;) 131 //@ invariant this.c |−> c& ∗&[ ]c. Channel()& ∗& [1/2]atomic space( c l i e n t i n v(c))& ∗& [1/2]Program receiveCount(i)& ∗& [1/3] Program messageMaxCount(mmc); 132 { 133 /∗@ 134 p r e d i c a t eP clientReceive()= [1/2]Program receiveCount(i)& ∗& [1/3] Program messageMaxCount(mmc); 135 136 p r e d i c a t eQ clientReceive(Objectr)= [1/2]Program receiveCount(r == n u l l?i:i+ 1)& ∗& [1/3]Program messageMaxCount(mmc); 137 138 lemma void my clientReceive() 139 r e q u i r e si < mmc& ∗&P clientReceive()& ∗& my unsep pred(c)(?items, ?qms); 140 e n s u r e sQ clientReceive(items != nil? head(items): null)& ∗& my unsep pred(c)(tail(items), qms); 141 { 142 openP clientReceive(); 143 open my unsep pred(c)(items, qms); 144 switch(items) { 145 c a s e nil: 146 c l o s eQ clientReceive(null); 147 c l o s e my unsep pred(c)(items, qms); 148 c a s e cons(head, tail): 149 Program. receiveCount++; 150 c l o s eQ clientReceive(head); 151 c l o s e my unsep pred(c)(tail, qms); 152 } 153 } 154 @∗/ 155 //@ produce lemma function pointer chunk(my sep): channel s e p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 156 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 157 //@ close my sep pred(); 158 //@ produce lemma function pointer chunk(my clientReceive): c h a n n e l r e c e i v e(client i n v(c),c, my unsep pred(c),P clientReceive, Q clientReceive)() { c a l l(); } ; 159 //@ closeP clientReceive(); 160 String m = this . c . receive (); 161 //@ openQ clientReceive(m); 162 i f ( m != null ) break ; 163 } 164 } 165 //@ close post(); 166 } 167 168 } 169 170 public class Program { 171 //@ static int sendCount; 172 //@ static int receiveCount;

106 173 public static int messageMaxCount ; 174 175 public static void main ( String [] args ) 176 //@ requires class i n i t t o k e n(Program.class)& ∗& Program messageMaxCount(?mmc) &∗&0 < mmc; 177 //@ ensures Program messageMaxCount(mmc)& ∗&[ ] Program sendCount(?sc)& ∗&[ ] Program receiveCount(?rc)& ∗& mmc == sc& ∗& mmc == rc; 178 { 179 //@ init c l a s s(); 180 Channel c = new Channel (); 181 //@ close client i n v(c)(); 182 //@ create a t o m i c s p a c e(client i n v(c)); 183 //@ leakc. Channel(); 184 185 SenderThread sender = new SenderThread ( c ); 186 JoinableRunnable senderJoinable = ThreadingHelper . createJoinableRunnable ( sender ); 187 //@ close sender.pre(); 188 //@ senderJoinable.closeIt(); 189 Thread threadS = new Thread ( senderJoinable ); 190 threadS . start (); 191 192 ReceiverThread receiver = new ReceiverThread ( c ); 193 JoinableRunnable receiverJoinable = ThreadingHelper . createJoinableRunnable ( receiver ) ; 194 //@ close receiver.pre(); 195 //@ receiverJoinable.closeIt(); 196 Thread threadR = new Thread ( receiverJoinable ); 197 threadR . start (); 198 199 ThreadingHelper . join ( threadS , senderJoinable ); 200 //@ open sender.post(); 201 ThreadingHelper . join ( threadR , receiverJoinable ); 202 //@ open receiver.post(); 203 204 //@ assert sendCount == messageMaxCount; 205 //@ assert receiveCount == messageMaxCount; 206 //@ assert messageMaxCount <= messageMaxCount; 207 //@ assert sendCount == receiveCount; 208 } 209 }

A.4 Client - version 2 (IIIa)

The VeriFast files required for this verification are: • Client.java: source file belonging to the “Client” module implementation. • Client.jarsrc: implementation of the “Client” module (presented in A.4). • Channels.jarspec: specification of the “Channels” module (presented in A.1). • Channels.javaspec: source file belonging to the “Channels” module specification (pre- sented in A.2). • verifast.jarspec: Specification of the “verifast” module, supplied with VeriFast in the example folder. • verifast.javaspec: source file belonging to the “verifast” module specification, supplied with VeriFast in the example folder. Only the content of the updated file is presented in the next listing.

Listing 58: Channels.java

107 1 import channels . ∗ ; 2 import verifast . ∗ ; 3 4 /∗@ 5 f i x p o i n t boolean non n u l l(Objecto) { r e t u r no != null; } 6 7 p r e d i c a t e c t o r client i n v(Channelc)()= 8 c. ChannelState(?items,?qms)& ∗& forall(items, non n u l l) == true; 9 10 p r e d i c a t e my sep pred()= true; 11 12 p r e d i c a t e c t o r my unsep pred(Channelc)(list items, int qms)= 13 f o r a l l(items, non n u l l) == true; 14 15 16 lemma void my sep() 17 r e q u i r e s exists (?c)& ∗& my sep pred()& ∗& client i n v(c)(); 18 e n s u r e sc. ChannelState(?items,?qms)& ∗& my unsep pred(c)(items, qms); 19 { 20 open client i n v(c)(); 21 a s s e r tc. ChannelState(?items,?qms); 22 c l o s e my unsep pred(c)(items, qms); 23 } 24 25 lemma void my unsep() 26 r e q u i r e s exists (?c)& ∗& my unsep pred(c)(?items,?qms)& ∗&c. ChannelState( items, qms); 27 e n s u r e s my sep pred()& ∗& client i n v(c)(); 28 { 29 open my unsep pred(c)( , ); 30 c l o s e client i n v(c)(); 31 c l o s e my sep pred(); 32 } 33 @∗/ 34 /∗@ 35 f i x p o i n t int mul(intx, inty) { r e t u r nx ∗ y; } 36 37 lemma void note(booleanb) 38 r e q u i r e sb; 39 e n s u r e sb; 40 { 41 } 42 43 lemma void mul lemma(intx, inty, intz) 44 r e q u i r e sy ==z; 45 e n s u r e sx ∗ y ==x ∗ z; 46 { 47 note(mul(x,y) == mul(x,z)); 48 note(mul(x,y) ==x ∗ y); 49 note(mul(x,z) ==x ∗ z); 50 } 51 @∗/ 52 53 public class SenderThread implements Runnable { 54 //@ int senderSendCount; 55 Thread thread ; 56 JoinableRunnable joinable ; 57 58 //@ predicate pre()=[ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space (client i n v(c))& ∗&[ ] Program sendMaxCount(?smc)& ∗& this. senderSendCount |−> ? s s c& ∗& ssc==0& ∗&0 < smc; 59 //@ predicate post()=[ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space (client i n v(c))& ∗&[ ] Program sendMaxCount(?smc)& ∗& this. senderSendCount |−> ? s s c& ∗& ssc==smc; 60 61 public void run () 62 //@ requires pre();

108 63 //@ ensures post(); 64 { 65 String m =”Hello”; 66 for ( int i=0; i < Program . sendMaxCount ; i++) 67 //@ invariant[ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space( c l i e n t i n v(c))& ∗& this. senderSendCount |−> ?ssc& ∗&[ ] Program sendMaxCount(?smc)& ∗&i == ssc& ∗&i <= smc; 68 { 69 for (;;) 70 //@ invariant[ ]Program channel(c)& ∗&[ ]c. Channel()& ∗&[ ] atomic space(client i n v(c))& ∗& this. senderSendCount |−> s s c& ∗&[ ] Program sendMaxCount(smc)& ∗&i < smc& ∗&i == ssc; 71 { 72 /∗@ 73 p r e d i c a t eP c l i e n t S e n d()= this. senderSendCount |−> s s c& ∗&[ ] Program sendMaxCount(smc)& ∗& ssc < smc; 74 p r e d i c a t eQ clientSend(booleanr)= this. senderSendCount |−> ?ssc2& ∗& s s c 2 ==(r? ssc+1: ssc)& ∗&[ ] Program sendMaxCount(smc); 75 76 lemma void my clientSend(booleanr) 77 r e q u i r e si < smc& ∗&P c l i e n t S e n d()& ∗& my unsep pred(c)(?items,? qms); 78 e n s u r e sQ clientSend(r)& ∗& my unsep pred(c)(r? append(items, cons( m, nil)): items, qms); 79 { 80 openP c l i e n t S e n d(); 81 open my unsep pred(c)(items, qms); 82 i f(r) senderSendCount++; 83 84 f o r a l l a p p e n d(items, cons(m, nil), non n u l l); 85 c l o s e my unsep pred(c)(r? append(items, cons(m, nil)): items, qms) ; 86 c l o s eQ clientSend(r); 87 } 88 @∗/ 89 //@ produce lemma function pointer chunk(my sep): channel s e p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 90 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 91 //@ close my sep pred(); 92 //@ produce lemma function pointer chunk(my clientSend): channel send( c l i e n t i n v(c),c, my unsep pred(c),m,P clientSend,Q clientSend)(r ) { c a l l(); } ; 93 //@ closeP c l i e n t S e n d(); 94 boolean success = Program . channel . send ( m ); 95 //@ openQ clientSend(success); 96 i f ( success ) break ; 97 } 98 } 99 //@ assert senderSendCount == Program. sendMaxCount; 100 //@ close post(); 101 } 102 } 103 /∗@ 104 p r e d i c a t e senderWorker(unitu, SenderThreads; unit u2)= 105 s.thread |−> ?t& ∗&s.joinable |−> ?j& ∗& 106 t.Thread(j, true)& ∗&j. JoinableRunnable(s, true)& ∗&s.getClass() == SenderThread. c l a s s& ∗& 107 u2 == unit; 108 @∗/ 109 110 public class Program { 111 //@ static int sendCount; 112 //@ static int receiveCount; 113

109 114 public static Channel channel ; 115 public static int sendersCount ; 116 public static int receiversCount ; 117 public static int sendMaxCount ; 118 public static int receiveMaxCount ; 119 120 public static void main ( String [] args ) 121 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 122 Program channel(?channel)& ∗& 123 Program sendersCount(?sendersCount)& ∗& Program receiversCount(? receiversCount)& ∗& 124 0 < sendersCount& ∗&0 < receiversCount& ∗& 125 Program sendCount(?psc)& ∗& Program receiveCount(?prc)& ∗& psc==0& ∗& prc==0 &∗& 126 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& Program receiveMaxCount(?rmc) &∗&0 < rmc;@ ∗/ 127 //@ ensures true; 128 { 129 Program . sendersCount = 1000; 130 Program . receiversCount = 1000; 131 Program . sendMaxCount = 2000; 132 Program . receiveMaxCount = 2000; 133 Program . work (); 134 //@assert Program. sendCount == 2000000; 135 //@assert Program. receiveCount == 2000000; 136 } 137 138 public static void work () 139 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 140 Program channel(?channel)& ∗& 141 Program sendCount(?psc)& ∗& Program receiveCount(?prc)& ∗& psc==0& ∗& prc==0 &∗& 142 [ ]Program sendersCount(?sendersCount)& ∗&0 < sendersCount& ∗&[ ] Program receiversCount(?receiversCount)& ∗&0 < receiversCount& ∗& 143 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& Program receiveMaxCount(?rmc) &∗&0 < rmc;@ ∗/ 144 /∗@ ensures Program sendCount(smc ∗ sendersCount)& ∗& Program receiveCount(rmc ∗ receiversCount);@ ∗/ 145 { 146 Program . channel = new Channel (); 147 //@ close client i n v(Program.channel)(); 148 //@ create a t o m i c s p a c e(client i n v(Program.channel)); 149 150 SenderThread [] senders = new SenderThread [ Program . sendersCount ]; 151 //@ array s l i c e d e e p e m p t y c l o s e(senders, 0, senderWorker, unit); 152 for ( int i = 0 ; i < Program . sendersCount ; i++) 153 /∗@ invariant0 <=i& ∗&i <= sendersCount& ∗& 154 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗& 155 [ ] Program sendMaxCount(smc)& ∗&0 < smc& ∗& 156 [ ]Program sendersCount(sendersCount)& ∗&0 < sendersCount& ∗& 157 a r r a y s l i c e(senders,i, sendersCount,?elems)& ∗& all e q(elems, null) == t r u e& ∗& 158 a r r a y s l i c e d e e p(senders, 0,i, senderWorker, unit, , ); 159 @∗/ 160 { 161 SenderThread s = new SenderThread (); 162 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( s ); 163 //@ closes.pre(); 164 //@j.closeIt(); 165 Thread t = new Thread ( j ); 166 s . thread = t ; 167 s . joinable = j ; 168 t . start (); 169 170 senders [ i ] = s ; 171 //@ array s l i c e s p l i t(senders,i,i+ 1);

110 172 //@ close senderWorker(unit, senders[i], unit); 173 //@ array s l i c e d e e p c l o s e(senders,i, senderWorker, unit); 174 } 175 176 ReceiverThread [] receivers = new ReceiverThread [ Program . receiversCount ]; 177 //@ array s l i c e d e e p e m p t y c l o s e(receivers, 0, receiverWorker, unit); 178 for ( int i = 0 ; i < Program . receiversCount ; i++) 179 /∗@ invariant0 <=i& ∗&i <= receiversCount& ∗& 180 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗& 181 [ ]Program receiveMaxCount(rmc)& ∗&0 < rmc& ∗& 182 [ ]Program receiversCount(receiversCount)& ∗&0 < receiversCount& ∗& 183 a r r a y s l i c e(receivers,i, receiversCount,?elems)& ∗& all e q(elems, null ) == true& ∗& 184 a r r a y s l i c e d e e p(receivers, 0,i, receiverWorker, unit, , ); 185 @∗/ 186 { 187 ReceiverThread r = new ReceiverThread (); 188 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( r ); 189 //@ closer.pre(); 190 //@j.closeIt(); 191 Thread t = new Thread ( j ); 192 r . thread = t ; 193 r . joinable = j ; 194 t . start (); 195 196 receivers [ i ] = r ; 197 //@ array s l i c e s p l i t(receivers,i,i+ 1); 198 //@ close receiverWorker(unit, receivers[i], unit); 199 //@ array s l i c e d e e p c l o s e(receivers,i, receiverWorker, unit); 200 } 201 202 for ( int j = 0 ; j < Program . sendersCount ; j++) 203 /∗@ invariant0 <=j& ∗&j <= sendersCount& ∗& 204 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗& 205 [ ]Program sendersCount(sendersCount)& ∗&0 < sendersCount& ∗& 206 Program sendCount(j ∗ smc)& ∗&[ ] Program sendMaxCount(smc)& ∗& 207 a r r a y s l i c e d e e p(senders,j, sendersCount, senderWorker, unit, , ); 208 @∗/ 209 { 210 SenderThread sw = senders [ j ]; 211 ThreadingHelper . join ( sw . thread , sw . joinable ); 212 //@ open sw.post(); 213 //@ Program. sendCount += sw. senderSendCount; 214 } 215 //@ assert Program. sendCount == Program. sendMaxCount ∗ sendersCount; 216 217 for ( int j = 0 ; j < Program . receiversCount ; j++) 218 /∗@ invariant0 <=j& ∗&j <= receiversCount& ∗& 219 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗& 220 [ ]Program receiversCount(receiversCount)& ∗&0 < receiversCount& ∗& 221 Program receiveCount(j ∗ rmc)& ∗&[ ]Program receiveMaxCount(rmc)& ∗& 222 a r r a y s l i c e d e e p(receivers,j, receiversCount, receiverWorker, unit, , ); 223 @∗/ 224 { 225 ReceiverThread sw = receivers [ j ]; 226 ThreadingHelper . join ( sw . thread , sw . joinable ); 227 //@ open sw.post(); 228 //@ Program. receiveCount += sw. receiverReceiveCount; 229 } 230 //@ assert Program. receiveCount == Program. receiveMaxCount ∗ receiversCount; 231 } 232 } 233

111 234 class ReceiverThread implements Runnable { 235 //@ int receiverReceiveCount; 236 Thread thread ; 237 JoinableRunnable joinable ; 238 239 //@ predicate pre()=[ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space (client i n v(c))& ∗&[ ]Program receiveMaxCount(?rmc)& ∗& this. receiverReceiveCount |−> ?rrc& ∗& rrc==0& ∗&0 < rmc; 240 //@ predicate post()=[ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space (client i n v(c))& ∗&[ ]Program receiveMaxCount(?rmc)& ∗& this. receiverReceiveCount |−> ?rrc& ∗& rrc==rmc; 241 242 public void run () 243 //@ requires pre(); 244 //@ ensures post(); 245 { 246 for ( int i=0; i < Program . receiveMaxCount ; i++) 247 //@ invariant[ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space( c l i e n t i n v(c))& ∗& this. receiverReceiveCount |−> ?rrc& ∗&[ ] Program receiveMaxCount(?rmc)& ∗&i <= rmc& ∗&i == rrc; 248 { 249 for (;;) 250 //@ invariant[ ]Program channel(c)& ∗&[ ]c. Channel()& ∗&[ ] atomic space(client i n v(c))& ∗& this. receiverReceiveCount |−> r r c &∗&[ ]Program receiveMaxCount(rmc)& ∗&i < rmc& ∗&i == rrc; 251 { 252 /∗@ 253 p r e d i c a t eP()= this. receiverReceiveCount |−> r r c& ∗&[ ] Program receiveMaxCount(rmc)& ∗& rrc < rmc; 254 255 p r e d i c a t eQ(Objectr)= this. receiverReceiveCount |−> ?rrc2& ∗& rrc2 == (r == null? rrc: rrc+ 1)& ∗&[ ]Program receiveMaxCount(rmc); 256 257 lemma void my c h a n n e l r e c e i v e() 258 r e q u i r e si < rmc& ∗&P()& ∗& my unsep pred(c)(?items,?qms); 259 e n s u r e sQ(items != nil? head(items): null)& ∗& my unsep pred(c)( t a i l(items), qms); 260 { 261 openP(); 262 open my unsep pred(c)(items, qms); 263 switch(items) { 264 c a s e nil: 265 c l o s e my unsep pred(c)(items, qms); 266 c l o s eQ(null); 267 c a s e cons(head, tail): 268 receiverReceiveCount++; 269 c l o s e my unsep pred(c)(tail, qms); 270 c l o s eQ(head); 271 } 272 } 273 @∗/ 274 //@ produce lemma function pointer chunk(my sep): channel s e p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 275 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 276 //@ close my sep pred(); 277 //@ produce lemma function pointer chunk(my c h a n n e l r e c e i v e): c h a n n e l r e c e i v e(client i n v(c),c, my unsep pred(c),P,Q)() { c a l l() ; } ; 278 //@ closeP(); 279 String m = Program . channel . receive (); 280 //@ openQ(m); 281 i f ( m != null ) break ; 282 } 283 }

112 284 //@ assert receiverReceiveCount == Program. receiveMaxCount; 285 //@ close post(); 286 } 287 } 288 /∗@ 289 p r e d i c a t e receiverWorker(unitu, ReceiverThreadr; unit u2)= 290 r.thread |−> ?t& ∗&r.joinable |−> ?j& ∗& 291 t.Thread(j, true)& ∗&j. JoinableRunnable(r, true)& ∗&r.getClass() == ReceiverThread .class& ∗& 292 u2 == unit; 293 @∗/

A.5 Client - version 3 (IIIb)

The VeriFast files required for this verification are: • Client.java: source file belonging to the “Client” module implementation. • Client.jarsrc: implementation of the “Client” module (presented in A.4). • Channels.jarspec: specification of the “Channels” module (presented in A.1). • Channels.javaspec: source file belonging to the “Channels” module specification (pre- sented in A.2). • verifast.jarspec: Specification of the “verifast” module, supplied with VeriFast in the example folder. • verifast.javaspec: source file belonging to the “verifast” module specification, supplied with VeriFast in the example folder. Only the content of the updated file is presented in the next listing.

Listing 59: Channels.java

1 import channels . ∗ ; 2 import verifast . ∗ ; 3 4 /∗@ 5 f i x p o i n t boolean non n u l l(Objecto) { r e t u r no != null; } 6 7 p r e d i c a t e c t o r client i n v(Channelc)()= 8 c. ChannelState(?items,?qms)& ∗& forall(items, non n u l l) == true& ∗& 9 [ 1 / 2 ] Program sendMaxCount(?smc)& ∗&0 < smc& ∗& 10 [ 1 / 2 ]Program receiveMaxCount(?rmc)& ∗&0 < rmc& ∗& 11 [ ]Program senders(?s)& ∗& 12 [ ]Program sendersCount(?sc)& ∗& 13 [ 1 / 3 ]array s l i c e d e e p(s, 0, sc, senderWorkerInv, unit, ,?lstSc)& ∗& 14 [ ]Program r e c e i v e r s(?r)& ∗& 15 [ ]Program receiversCount(?rc)& ∗& 16 [ 1 / 3 ]array s l i c e d e e p(r, 0, rc, receiverWorkerInv, unit, ,?lstRc)& ∗& 17 //sum(lstSc) <= smc ∗ sc& ∗& sum(lstRc) <= rmc ∗ r c& ∗& 18 l e n g t h(items) == sum(lstSc) − sum(lstRc)& ∗& 19 sum(lstRc) <= sum(lstSc); 20 21 p r e d i c a t e senderWorkerInv(unitu, SenderThread sw; int senderCount)= 22 [ 3 / 2 ]sw. senderSendCount |−> senderCount; 23 // [3/2]sw. senderSendCount |−> ?senderCount1& ∗& senderCount == senderCount1+ 1; 24 25 p r e d i c a t e receiverWorkerInv(unitu, ReceiverThread rw; int receiverCount)= 26 [ 3 / 2 ]rw. receiverReceiveCount |−> receiverCount; 27 28 f i x p o i n t int sum(list vs) {

113 29 switch(vs) { 30 c a s e nil: return 0; 31 c a s e cons(h,t): returnh+ sum(t); 32 } 33 } 34 35 lemma auto void sum a l l e q(list vs) 36 r e q u i r e s all e q(vs, 0) == true; 37 e n s u r e s sum(vs) == 0; 38 { 39 switch(vs) { 40 c a s e nil: 41 c a s e cons(x0, xs0): sum a l l e q(xs0); 42 } 43 } 44 45 lemma void sum take cons drop(intk, list xs, intx) 46 r e q u i r e s0 <=k& ∗&k < l e n g t h(xs); 47 e n s u r e s sum(xs) == sum(append(take(k, xs), cons(x, drop(k+ 1, xs)))) − x+ head( take(1, drop(k, xs))); 48 { 49 switch(xs) { 50 c a s e nil: 51 c a s e cons(x0, xs0): 52 i f(k == 0) { 53 } e l s e { 54 sum take cons drop(k − 1 , xs0,x); 55 } 56 } 57 } 58 59 p r e d i c a t e my sep pred()= true; 60 61 p r e d i c a t e c t o r my unsep pred(Channelc)(list items, int qms)= 62 f o r a l l(items, non n u l l) == true& ∗& 63 [ 1 / 2 ] Program sendMaxCount(?smc)& ∗&0 < smc& ∗& 64 [ 1 / 2 ]Program receiveMaxCount(?rmc)& ∗&0 < rmc& ∗& 65 [ ]Program senders(?s)& ∗& 66 [ ]Program sendersCount(?sc)& ∗& 67 [ 1 / 3 ]array s l i c e d e e p(s, 0, sc, senderWorkerInv, unit, ,?lstSc)& ∗& 68 [ ]Program r e c e i v e r s(?r)& ∗& 69 [ ]Program receiversCount(?rc)& ∗& 70 [ 1 / 3 ]array s l i c e d e e p(r, 0, rc, receiverWorkerInv, unit, ,?lstRc)& ∗& 71 //sum(lstSc) <= smc ∗ sc& ∗& sum(lstRc) <= rmc ∗ r c& ∗& 72 l e n g t h(items) == sum(lstSc) − sum(lstRc)& ∗& 73 sum(lstRc) <= sum(lstSc); 74 75 lemma void my sep() 76 r e q u i r e s exists (?c)& ∗& my sep pred()& ∗& client i n v(c)(); 77 e n s u r e sc. ChannelState(?items,?qms)& ∗& my unsep pred(c)(items, qms); 78 { 79 open client i n v(c)(); 80 a s s e r tc. ChannelState(?items,?qms); 81 c l o s e my unsep pred(c)(items, qms); 82 } 83 84 lemma void my unsep() 85 r e q u i r e s exists (?c)& ∗& my unsep pred(c)(?items,?qms)& ∗&c. ChannelState( items, qms); 86 e n s u r e s my sep pred()& ∗& client i n v(c)(); 87 { 88 open my unsep pred(c)( , ); 89 c l o s e client i n v(c)(); 90 c l o s e my sep pred(); 91 } 92 93 @∗/

114 94 /∗@ 95 96 f i x p o i n t int mul(intx, inty) { r e t u r nx ∗ y; } 97 98 lemma void note(booleanb) 99 r e q u i r e sb; 100 e n s u r e sb; 101 { 102 } 103 104 lemma void mul lemma(intx, inty, intz) 105 r e q u i r e sy ==z; 106 e n s u r e sx ∗ y ==x ∗ z; 107 { 108 note(mul(x,y) == mul(x,z)); 109 note(mul(x,y) ==x ∗ y); 110 note(mul(x,z) ==x ∗ z); 111 } 112 113 p r e d i c a t e foreach i (inti, list xs, predicate(int,t)p)= 114 switch(xs) { 115 c a s e nil: return true; 116 c a s e cons(x0, xs0): returnp(i, x0)& ∗& foreach i(i+ 1, xs0,p); 117 } ; 118 119 lemma void foreach i a p p e n d (inti, list xs, list ys) 120 r e q u i r e s foreach i(i, xs,?p)& ∗& foreach i(i+ length(xs), ys,p); 121 e n s u r e s foreach i(i, append(xs, ys),p); 122 { 123 switch(xs) { 124 c a s e nil: 125 c a s e cons(x0, xs0): 126 open foreach i(i, xs,p); 127 f o r e a c h i a p p e n d(i+ 1, xs0, ys); 128 c l o s e foreach i(i, append(xs, ys),p); 129 } 130 } 131 @∗/ 132 133 public class SenderThread implements Runnable { 134 //@ int senderSendCount; 135 //@ int myIndex; 136 Thread thread ; 137 JoinableRunnable joinable ; 138 139 /∗@ predicate pre()= 140 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v(c))& ∗& 141 [ ] Program sendMaxCount(?smc)& ∗&0 < smc& ∗& 142 [ ]Program senders(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 143 [ ]Program sendersCount(?sc)& ∗& myIndex < sc& ∗& 144 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, n i l), cons(0, nil)); 145 @∗/ 146 /∗@ predicate post()= 147 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v(c))& ∗& 148 [ ] Program sendMaxCount(?smc)& ∗& 149 [ ]Program senders(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 150 [ ]Program sendersCount(?sc)& ∗& myIndex < sc& ∗& 151 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, n i l), cons(smc, nil)); 152 @∗/ 153 154 public void run () 155 //@ requires pre(); 156 //@ ensures post(); 157 { 158 String m =”Hello”;

115 159 int i ; 160 for ( i=0; i < Program . sendMaxCount ; i++) 161 /∗@ invariant 162 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v(c)) &∗& 163 [ ] Program sendMaxCount(?smc)& ∗&i <= smc& ∗& 164 [ ]Program senders(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 165 [ ]Program sendersCount(?sc)& ∗& myIndex < sc& ∗& 166 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons( t h i s, nil), cons(i, nil)); 167 @∗/ 168 { 169 for (;;) 170 /∗@ invariant 171 [ ]Program channel(c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v( c))& ∗& 172 [ ] Program sendMaxCount(smc)& ∗&i < smc& ∗& 173 [ ]Program senders(s)& ∗&[ ]this. myIndex |−> myIndex& ∗& 174 [ ]Program sendersCount(sc)& ∗& myIndex < sc& ∗& 175 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit, cons(this, nil), cons(i, nil)); 176 @∗/ 177 { 178 179 /∗@ 180 p r e d i c a t eP c l i e n t S e n d()= 181 [ ] Program sendMaxCount(smc)& ∗& 182 [ ]Program senders(s)& ∗&[ ]this. myIndex |−> myIndex& ∗& 183 [ ]Program sendersCount(sc)& ∗& myIndex < sc& ∗& 184 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit , cons(this, nil), cons(i, nil)); 185 p r e d i c a t eQ clientSend(booleanr)= 186 [ ] Program sendMaxCount(smc)& ∗& 187 [ ]Program senders(s)& ∗&[ ]this. myIndex |−> myIndex& ∗& 188 [ ]Program sendersCount(sc)& ∗& myIndex < sc& ∗& 189 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, senderWorkerInv, unit , cons(this, nil),(r? cons(i+1, nil): cons(i, nil))); 190 191 192 lemma void my send(booleanr) 193 r e q u i r e si < smc& ∗&P c l i e n t S e n d()& ∗& my unsep pred(c)(?items,? qms); 194 e n s u r e sQ clientSend(r)& ∗& my unsep pred(c)(r? append(items, cons( m, nil)): items, qms); 195 { 196 openP c l i e n t S e n d(); 197 open my unsep pred(c)(items, qms); 198 SenderThread[] senders= Program.senders; 199 i n t sendersCount= Program. sendersCount; 200 i n tk= myIndex; 201 a s s e r t [1/3]array s l i c e d e e p(senders, 0, sendersCount, senderWorkerInv, unit, ,?oldSenderCounts); 202 a r r a y s l i c e d e e p s p l i t(Program.senders, 0, myIndex); 203 a r r a y s l i c e d e e p s p l i t p r e c i s e(1/3, Program.senders, myIndex, Program. sendersCount, senderWorkerInv, unit, myIndex+ 1); 204 a r r a y s l i c e d e e p o p e n p r e c i s e(2/3, Program.senders, myIndex); 205 i n t myOldSendCount= senderSendCount; 206 i f(r) { 207 senderSendCount++; 208 } 209 i n t myNewSendCount= senderSendCount; 210 a r r a y s l i c e d e e p c l o s e(Program.senders, myIndex, senderWorkerInv, u n i t); 211 a r r a y s l i c e d e e p j o i n p r e c i s e(1/3, Program.senders, myIndex, myIndex + 1, senderWorkerInv, unit, Program. sendersCount); 212 a r r a y s l i c e d e e p j o i n p r e c i s e(1/3, Program.senders, 0, myIndex, senderWorkerInv, unit, Program. sendersCount);

116 213 214 f o r a l l a p p e n d(items, cons(m, nil), non n u l l); 215 a s s e r t [1/3]array s l i c e d e e p(senders, 0, sendersCount, senderWorkerInv, unit, ,?newSenderCounts); 216 a s s e r t newSenderCounts == append(take(k, oldSenderCounts), cons( myNewSendCount, drop(1, drop(k, oldSenderCounts)))); 217 a s s e r t length(oldSenderCounts) == sendersCount; 218 a s s e r t0 <=1& ∗&0 <=k& ∗&1+k <= length(oldSenderCounts); 219 drop drop(1,k, oldSenderCounts); 220 a s s e r t newSenderCounts == append(take(k, oldSenderCounts), cons( myNewSendCount, drop(k+ 1, oldSenderCounts))); 221 sum take cons drop(k, oldSenderCounts, myNewSendCount); 222 a s s e r t sum(newSenderCounts) == sum(oldSenderCounts)+ myNewSendCount − myOldSendCount; 223 c l o s e my unsep pred(c)(r? append(items, cons(m, nil)): items, qms) ; 224 c l o s eQ clientSend(r); 225 } 226 @∗/ 227 //@ produce lemma function pointer chunk(my sep): channel s e p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 228 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 229 //@ close my sep pred(); 230 //@ produce lemma function pointer chunk(my send): channel send( c l i e n t i n v(c),c, my unsep pred(c),m,P clientSend,Q clientSend)(r ) { c a l l(); } ; 231 //@ closeP c l i e n t S e n d(); 232 boolean success = Program . channel . send ( m ); 233 //@ openQ clientSend(success); 234 i f ( success ) break ; 235 } 236 } 237 //@ asserti == Program. sendMaxCount; 238 //@ close post(); 239 } 240 } 241 /∗@ 242 p r e d i c a t e senderWorker(unitu, SenderThread SenderWorker; unit u2)= 243 [ 3 ] SenderWorker.thread |−> ?t& ∗& [3] SenderWorker.joinable |−> ?j& ∗& 244 [ 3 ]t.Thread(j, true)& ∗& [3]j. JoinableRunnable(SenderWorker, true)& ∗& 245 SenderWorker.getClass() == SenderThread.class& ∗& 246 u2 == unit; 247 248 p r e d i c a t e senderWorkerNull(unitu, SenderThread SenderWorker; unit u2)= 249 [ 3 / 2 ] SenderWorker.thread |−> &∗& [3/2] SenderWorker.joinable |−> &∗& 250 [ 3 / 2 ] SenderWorker. myIndex |−> &∗& 251 SenderWorker.getClass() == SenderThread.class& ∗& 252 [ 3 / 4 ] SenderWorker. senderSendCount |−> 0& ∗& 253 u2 == unit; 254 255 p r e d i c a t e senderWorkerIndex(inti, SenderThreadt)= 256 [ ]t. myIndex |−> i; 257 @∗/ 258 259 public class Program { 260 261 //@ static int sendCount; 262 //@ static int receiveCount; 263 264 public static int sendMaxCount ; 265 public static int receiveMaxCount ; 266 public static Channel channel ; 267 public static int sendersCount ; 268 public static int receiversCount ;

117 269 public static SenderThread [] senders ; 270 public static ReceiverThread [] receivers ; 271 272 public static void main ( String [] args ) 273 274 /∗@ requires class i n i t t o k e n(Program.class)& ∗& 275 Program channel(?channel)& ∗& 276 Program senders( )& ∗& Program r e c e i v e r s( )& ∗& 277 [ ]Program sendersCount(?sendersCount)& ∗&0 < sendersCount& ∗& 278 [ ]Program receiversCount(?receiversCount)& ∗&0 < receiversCount& ∗& 279 Program sendCount(?psc)& ∗& psc==0& ∗& 280 Program receiveCount(?prc)& ∗& prc==0& ∗& 281 Program sendMaxCount(?smc)& ∗&0 < smc& ∗& 282 Program receiveMaxCount(?rmc)& ∗&0 < rmc& ∗& rmc==smc& ∗& sendersCount== receiversCount;@ ∗/ 283 /∗@ ensures Program sendCount(?sc)& ∗& Program receiveCount(?rc) 284 &∗& sc ==(smc ∗ sendersCount)& ∗& rc ==(rmc ∗ receiversCount) 285 &∗& sc == rc 286 ;@ ∗/ 287 { 288 Program . senders = new SenderThread [ Program . sendersCount ]; 289 //@ SenderThread[]s= Program.senders; 290 //@ leak Program senders(s); 291 //@ array s l i c e d e e p e m p t y c l o s e(s, 0, senderWorker, unit); 292 for ( int i = 0 ; i < Program . sendersCount ; i++) 293 /∗@ invariant0 <=i& ∗&i <= sendersCount& ∗& 294 [ ]Program senders(s)& ∗&[ ]Program sendersCount(sendersCount)& ∗& 295 [ 1 / 3 ] Program sendMaxCount(smc)& ∗&0 < smc& ∗& 296 a r r a y s l i c e(s,i, sendersCount,?elems)& ∗& all e q(elems, null) == true &∗& 297 [ 1 / 3 ]array s l i c e d e e p(s, 0,i, senderWorkerInv, unit, ,?v)& ∗& all e q( v, 0) == true& ∗& 298 [ 2 / 3 ]array s l i c e d e e p(s, 0,i, senderWorkerNull, unit, , ); 299 @∗/ 300 { 301 Program . senders [ i ] = new SenderThread (); 302 //@ Program.senders[i]. myIndex=i; 303 //@ array s l i c e s p l i t(senders,i,i+ 1); 304 //@ close [2/3] senderWorkerNull(unit, senders[i], unit); 305 //@ close [1/3] senderWorkerInv(unit, senders[i], ); 306 //@ array s l i c e d e e p c l o s e p r e c i s e(2/3, senders,i, senderWorkerNull, unit); 307 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3, senders,i, senderWorkerInv, unit); 308 309 } 310 311 Program . receivers = new ReceiverThread [ Program . receiversCount ]; 312 //@ ReceiverThread[]r= Program.receivers; 313 //@ leak Program r e c e i v e r s(r); 314 //@ array s l i c e d e e p e m p t y c l o s e(r, 0, receiverWorker, unit); 315 for ( int i = 0 ; i < Program . receiversCount ; i++) 316 /∗@ invariant0 <=i& ∗&i <= receiversCount& ∗& 317 [ ]Program r e c e i v e r s(r)& ∗&[ ]Program receiversCount(receiversCount) &∗& 318 [ 1 / 3 ]Program receiveMaxCount(rmc)& ∗&0 < rmc& ∗& 319 a r r a y s l i c e(r,i, receiversCount,?elems)& ∗& all e q(elems, null) == t r u e& ∗& 320 [ 1 / 3 ]array s l i c e d e e p(r, 0,i, receiverWorkerInv, unit, ,?v)& ∗& a l l e q(v, 0) == true& ∗& 321 [ 2 / 3 ]array s l i c e d e e p(r, 0,i, receiverWorkerNull, unit, , ); 322 @∗/ 323 { 324 receivers [ i ] = new ReceiverThread (); 325 //@ Program.receivers[i]. myIndex=i; 326 //@ array s l i c e s p l i t(receivers,i,i+ 1); 327 //@ close [2/3] receiverWorkerNull(unit, receivers[i], unit); 328 //@ close [1/3] receiverWorkerInv(unit, receivers[i], );

118 329 //@ array s l i c e d e e p c l o s e p r e c i s e(2/3, receivers,i, receiverWorkerNull, u n i t); 330 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3, receivers,i, receiverWorkerInv, u n i t); 331 } 332 Program . channel = new Channel (); 333 334 //@ close client i n v(Program.channel)(); 335 //@ create a t o m i c s p a c e(client i n v(Program.channel)); 336 //@ close foreach i(0, nil, senderWorkerIndex); 337 338 for ( int i = 0 ; i < Program . sendersCount ; i++) 339 /∗@ invariant0 <=i& ∗&i <= sendersCount& ∗& 340 [ ]Program sendersCount(sendersCount)& ∗&[ ]Program senders(s)& ∗& 341 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗&[ ] Program sendMaxCount(smc)& ∗&0 < smc& ∗& 342 [ 2 / 3 ]array s l i c e d e e p(s,i, sendersCount, senderWorkerNull, unit, , ) &∗& 343 [ 1 / 3 ]array s l i c e d e e p(s, 0,i, senderWorker, unit,?elements, )& ∗& f o r e a c h i(0, elements, senderWorkerIndex); 344 @∗/ 345 { 346 //@ array s l i c e d e e p s p l i t(s,i,i+ 1); 347 //@ array s l i c e d e e p o p e n p r e c i s e(2/3,s,i); 348 SenderThread sender = senders [ i ]; 349 //@ close [1/3] senderWorkerInv(unit, sender, ); 350 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3,s,i, senderWorkerInv, unit); 351 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( sender ); 352 //@ sender. myIndex=i; 353 //@ leak sender. myIndex |−> i; 354 //@ close sender.pre(); 355 //@j.closeIt(); 356 Thread t = new Thread ( j ); 357 t . start (); 358 359 sender . thread = t ; 360 sender . joinable = j ; 361 //@ close [1/3] senderWorker(unit, sender, unit); 362 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3, senders,i, senderWorker, unit); 363 364 //@ close foreach i(i+ 1, nil, senderWorkerIndex); 365 //@ close senderWorkerIndex(i, sender); 366 //@ close foreach i(i, cons(sender, nil), senderWorkerIndex); 367 //@ foreach i a p p e n d(0, elements, cons(sender, nil)); 368 } 369 //@ close foreach i(0, nil, receiverWorkerIndex); 370 for ( int i = 0 ; i < Program . receiversCount ; i++) 371 /∗@ invariant0 <=i& ∗&i <= receiversCount& ∗& 372 [ ]Program receiversCount(receiversCount)& ∗&[ ]Program r e c e i v e r s(r) &∗& 373 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗&[ ]Program receiveMaxCount(rmc)& ∗&0 < rmc& ∗& 374 [ 2 / 3 ]array s l i c e d e e p(r,i, receiversCount, receiverWorkerNull, unit, , )& ∗& 375 [ 1 / 3 ]array s l i c e d e e p(r, 0,i, receiverWorker, unit,?elements, )& ∗& f o r e a c h i(0, elements, receiverWorkerIndex); 376 @∗/ 377 { 378 //@ array s l i c e d e e p s p l i t(r,i,i+ 1); 379 //@ array s l i c e d e e p o p e n p r e c i s e(2/3,r,i); 380 ReceiverThread receiver = receivers [ i ]; 381 //@ close [1/3] receiverWorkerInv(unit, receiver, ); 382 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3,r,i, receiverWorkerInv, unit); 383 JoinableRunnable j = ThreadingHelper . createJoinableRunnable ( receiver ); 384 //@ receiver. myIndex=i; 385 //@ leak receiver. myIndex |−> i; 386 //@ close receiver.pre();

119 387 //@j.closeIt(); 388 Thread t = new Thread ( j ); 389 t . start (); 390 391 receiver . thread = t ; 392 receiver . joinable = j ; 393 //@ close [1/3] receiverWorker(unit, receiver, unit); 394 //@ array s l i c e d e e p c l o s e p r e c i s e(1/3, receivers,i, receiverWorker, unit); 395 396 //@ close foreach i(i+ 1, nil, receiverWorkerIndex); 397 //@ close receiverWorkerIndex(i, receiver); 398 //@ close foreach i(i, cons(receiver, nil), receiverWorkerIndex); 399 //@ foreach i a p p e n d(0, elements, cons(receiver, nil)); 400 } 401 402 for ( int j = 0 ; j < Program . sendersCount ; j++) 403 /∗@ invariant0 <=j& ∗&j <= sendersCount& ∗& 404 [ ]Program sendersCount(sendersCount)& ∗&[ ]Program senders(s)& ∗& 405 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗& 406 Program sendCount(j ∗ smc)& ∗&[ ] Program sendMaxCount(smc)& ∗& 407 [ 1 / 3 ]array s l i c e d e e p(s,j, sendersCount, senderWorker, unit,?elements, )& ∗& foreach i(j, elements, senderWorkerIndex); 408 @∗/ 409 { 410 SenderThread sw = senders [ j ]; 411 ThreadingHelper . join ( sw . thread , sw . joinable ); 412 //@ open sw.post(); 413 //@ open foreach i(j, elements, senderWorkerIndex); 414 //@ open senderWorkerIndex(j, sw); 415 //@ int jj= sw. myIndex; 416 //@ assertj == jj; 417 //@ array s l i c e d e e p o p e n p r e c i s e(1/3,s,j); 418 //@ open senderWorkerInv(unit, sw, ); 419 //@ Program. sendCount += sw. senderSendCount; 420 } 421 //@ assert Program. sendCount == Program. sendMaxCount ∗ sendersCount; 422 for ( int j = 0 ; j < Program . receiversCount ; j++) 423 /∗@ invariant0 <=j& ∗&j <= receiversCount& ∗& 424 [ ]Program receiversCount(receiversCount)& ∗&[ ]Program r e c e i v e r s(r) &∗& 425 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v (c))& ∗& 426 Program receiveCount(j ∗ rmc)& ∗&[ ]Program receiveMaxCount(rmc)& ∗& 427 [ 1 / 3 ]array s l i c e d e e p(r,j, receiversCount, receiverWorker, unit,? elements, )& ∗& foreach i(j, elements, receiverWorkerIndex); 428 @∗/ 429 { 430 ReceiverThread rw = receivers [ j ]; 431 ThreadingHelper . join ( rw . thread , rw . joinable ); 432 //@ open rw.post(); 433 //@ open foreach i(j, elements, receiverWorkerIndex); 434 //@ open receiverWorkerIndex(j, rw); 435 //@ int jj= rw. myIndex; 436 //@ assertj == jj; 437 //@ array s l i c e d e e p o p e n p r e c i s e(1/3,r,j); 438 //@ open receiverWorkerInv(unit, rw, ); 439 //@ Program. receiveCount += rw. receiverReceiveCount; 440 } 441 //@ assert Program. receiveCount == Program. receiveMaxCount ∗ receiversCount; 442 } 443 444 } 445 446 447 class ReceiverThread implements Runnable { 448 //@ int receiverReceiveCount;

120 449 //@ int myIndex; 450 Thread thread ; 451 JoinableRunnable joinable ; 452 453 /∗@ predicate pre()= 454 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v(c))& ∗& 455 [ ]Program receiveMaxCount(?rmc)& ∗&0 < rmc& ∗& 456 [ ]Program r e c e i v e r s(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 457 [ ]Program receiversCount(?rc)& ∗& myIndex < r c& ∗& 458 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, receiverWorkerInv, unit, cons( t h i s, nil), cons(0, nil)); 459 @∗/ 460 /∗@ predicate post()= 461 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v(c))& ∗& 462 [ ]Program receiveMaxCount(?rmc)& ∗& 463 [ ]Program r e c e i v e r s(?s)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 464 [ ]Program receiversCount(?rc)& ∗& myIndex < r c& ∗& 465 [ 1 / 3 ]array s l i c e d e e p(s, myIndex, myIndex+ 1, receiverWorkerInv, unit, cons( t h i s, nil), cons(rmc, nil)); 466 @∗/ 467 468 public void run () 469 //@ requires pre(); 470 //@ ensures post(); 471 { 472 int i ; 473 for ( i=0; i < Program . receiveMaxCount ; i++) 474 /∗@ invariant 475 [ ]Program channel(?c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v(c)) &∗& 476 [ ]Program receiveMaxCount(?rmc)& ∗&i <= rmc& ∗& 477 [ ]Program r e c e i v e r s(?r)& ∗&[ ]this. myIndex |−> ?myIndex& ∗& 478 [ ]Program receiversCount(?rc)& ∗& myIndex < r c& ∗& 479 [ 1 / 3 ]array s l i c e d e e p(r, myIndex, myIndex+ 1, receiverWorkerInv, unit, cons (this, nil), cons(i, nil)); 480 @∗/ 481 { 482 for (;;) 483 /∗@ invariant 484 [ ]Program channel(c)& ∗&[ ]c. Channel()& ∗&[ ]atomic space(client i n v( c))& ∗& 485 [ ]Program receiveMaxCount(rmc)& ∗&i < rmc& ∗& 486 [ ]Program r e c e i v e r s(r)& ∗&[ ]this. myIndex |−> myIndex& ∗& 487 [ ]Program receiversCount(rc)& ∗& myIndex < r c& ∗& 488 [ 1 / 3 ]array s l i c e d e e p(r, myIndex, myIndex+ 1, receiverWorkerInv, unit, cons(this, nil), cons(i, nil)); 489 @∗/ 490 { 491 /∗@ 492 p r e d i c a t eP()= 493 [ ]Program receiveMaxCount(rmc)& ∗& 494 [ ]Program r e c e i v e r s(r)& ∗&[ ]this. myIndex |−> myIndex& ∗& 495 [ ]Program receiversCount(rc)& ∗& myIndex < r c& ∗& 496 [ 1 / 3 ]array s l i c e d e e p(r, myIndex, myIndex+ 1, receiverWorkerInv, unit, cons(this, nil), cons(i, nil)); 497 p r e d i c a t eQ(Object result)= 498 [ ]Program receiveMaxCount(rmc)& ∗& 499 [ ]Program r e c e i v e r s(r)& ∗&[ ]this. myIndex |−> myIndex& ∗& 500 [ ]Program receiversCount(rc)& ∗& myIndex < r c& ∗& 501 [ 1 / 3 ]array s l i c e d e e p(r, myIndex, myIndex+ 1, receiverWorkerInv, unit, cons(this, nil),(result != null? cons(i+ 1, nil): cons (i, nil))); 502 503 lemma void my c h a n n e l r e c e i v e() 504 r e q u i r e si < rmc& ∗&P()& ∗& my unsep pred(c)(?items,?qms); 505 e n s u r e sQ(items != nil? head(items): null)& ∗& my unsep pred(c)( t a i l(items), qms);

121 506 { 507 openP(); 508 open my unsep pred(c)(items, qms); 509 ReceiverThread[] receivers= Program.receivers; 510 i n t receiversCount= Program. receiversCount; 511 i n tk= myIndex; 512 a s s e r t [1/3]array s l i c e d e e p(receivers, 0, receiversCount, receiverWorkerInv, unit, ,?oldReceiverCounts); 513 a r r a y s l i c e d e e p s p l i t(Program.receivers, 0, myIndex); 514 a r r a y s l i c e d e e p s p l i t p r e c i s e(1/3, Program.receivers, myIndex, Program. receiversCount, receiverWorkerInv, unit, myIndex+ 1); 515 a r r a y s l i c e d e e p o p e n p r e c i s e(2/3, Program.receivers, myIndex); 516 i n t myOldReceiveCount= receiverReceiveCount; 517 switch(items) { 518 c a s e nil: 519 c a s e cons(head, tail): 520 receiverReceiveCount++; 521 } 522 i n t myNewReceiverCount= receiverReceiveCount; 523 a r r a y s l i c e d e e p c l o s e(Program.receivers, myIndex, receiverWorkerInv , unit); 524 a r r a y s l i c e d e e p j o i n p r e c i s e(1/3, Program.receivers, myIndex, myIndex+ 1, receiverWorkerInv, unit, Program. receiversCount); 525 a r r a y s l i c e d e e p j o i n p r e c i s e(1/3, Program.receivers, 0, myIndex, receiverWorkerInv, unit, Program. receiversCount); 526 527 //forall a p p e n d(items, cons(m, nil), non n u l l); 528 a s s e r t [1/3]array s l i c e d e e p(receivers, 0, receiversCount, receiverWorkerInv, unit, ,?newReceiverCounts); 529 a s s e r t newReceiverCounts == append(take(k, oldReceiverCounts), cons( myNewReceiverCount, drop(1, drop(k, oldReceiverCounts)))); 530 a s s e r t length(oldReceiverCounts) == receiversCount; 531 a s s e r t0 <=1& ∗&0 <=k& ∗&1+k <= length(oldReceiverCounts); 532 drop drop(1,k, oldReceiverCounts); 533 a s s e r t newReceiverCounts == append(take(k, oldReceiverCounts), cons( myNewReceiverCount, drop(k+ 1, oldReceiverCounts))); 534 sum take cons drop(k, oldReceiverCounts, myNewReceiverCount); 535 a s s e r t sum(newReceiverCounts) == sum(oldReceiverCounts)+ myNewReceiverCount − myOldReceiveCount; 536 537 switch(items) { 538 c a s e nil: 539 c l o s e my unsep pred(c)(items, qms); 540 c l o s eQ(null); 541 c a s e cons(head, tail): 542 c l o s e my unsep pred(c)(tail, qms); 543 c l o s eQ(head); 544 } 545 } 546 @∗/ 547 //@ produce lemma function pointer chunk(my sep): channel s e p( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 548 //@ produce lemma function pointer chunk(my unsep): channel unsep( c l i e n t i n v(c),c, my sep pred, my unsep pred(c))() { c l o s e exists(c) ; call(); } ; 549 //@ close my sep pred(); 550 //@ produce lemma function pointer chunk(my c h a n n e l r e c e i v e): c h a n n e l r e c e i v e(client i n v(c),c, my unsep pred(c),P,Q)() { c a l l() ; } ; 551 //@ closeP(); 552 String m = Program . channel . receive (); 553 //@ openQ(m); 554 i f ( m != null ) break ; 555 } 556 } 557 //@ close post();

122 558 } 559 } 560 561 /∗@ 562 p r e d i c a t e receiverWorker(unitu, ReceiverThread ReceiverWorker; unit u2)= 563 [ 3 ] ReceiverWorker.thread |−> ?t& ∗& [3] ReceiverWorker.joinable |−> ?j& ∗& 564 [ 3 ]t.Thread(j, true)& ∗& [3]j. JoinableRunnable(ReceiverWorker, true)& ∗& 565 ReceiverWorker.getClass() == ReceiverThread.class& ∗& 566 u2 == unit; 567 568 p r e d i c a t e receiverWorkerNull(unitu, ReceiverThread ReceiverWorker; unit u2)= 569 [ 3 / 2 ] ReceiverWorker.thread |−> &∗& [3/2] ReceiverWorker.joinable |−> &∗& 570 [ 3 / 2 ] ReceiverWorker. myIndex |−> &∗& 571 ReceiverWorker.getClass() == ReceiverThread.class& ∗& 572 [ 3 / 4 ] ReceiverWorker. receiverReceiveCount |−> 0& ∗& 573 u2 == unit; 574 575 p r e d i c a t e receiverWorkerIndex(inti, ReceiverThreadt)= 576 [ ]t. myIndex |−> i; 577 @∗/

A.6 Modifications to the VeriFast libraries

There were some changes made to the VeriFast Java libraries for the verification in 7.6. The file that is modified is “bin\rt\java.lang.javaspec”. The following lemmas are modified: the first, second and fifth. The other lemmas are added: the third, fourth, sixed and seventh.

Listing 60: java.lang.javaspec

1 lemma_auto void array_slice_deep_inv(); 2 requires [? f ] array_slice_deep(?array ,? start ,? end ,?p ,? info ,? elems ,? vs ) ; 3 ensures [ f ] array_slice_deep(array , start , end , p , info , elems , vs )&∗& array != null &∗& 0 <= start &∗& start <= end &∗& end <= array . length &∗& start + length ( elems ) == end &∗& start + length ( vs ) == end ; 4 5 lemma void array_slice_deep_close(T [] array , int start , predicate ( A , T ; V ) p , A a ); 6 requires [? f ] array_slice(array , start , start + 1 , ? elems )&∗&[ f ] p ( a , head ( elems ) ,?v ); 7 ensures [ f ] array_slice_deep(array , start , start + 1 , p , a , elems , cons ( v , nil )); 8 9 lemma void array_slice_deep_close_precise(real f , T [] array , int start , predicate ( A , T ; V ) p , A a ); 10 requires [ f ] array_slice(array , start , start + 1 , ? elems )&∗&[ f ] p ( a , head ( elems ), ?v ); 11 ensures [ f ] array_slice_deep(array , start , start + 1 , p , a , elems , cons ( v , nil )); 12 13 lemma void array_slice_deep_open_precise(real f , T [] array , int start ); 14 requires [ f ] array_slice_deep(array , start , start + 1 , ?p ,?a ,? elems ,? vs ); 15 ensures [ f ] array_slice(array , start , start + 1 , elems )&∗&[ f ] p ( a , head ( elems ), head ( vs ))&∗& elems == cons ( head ( elems ), tail ( elems ))&∗& vs == cons ( head ( vs ), tail ( vs )); 16 lemma void array_slice_deep_split(T [] array , int start , int start1 ); 17 requires [? f ] array_slice_deep(array , start ,? end ,?p ,?a ,? elems ,? vs )&∗& start <= start1 &∗& start1 <= end ; 18 ensures 19 [ f ] array_slice_deep(array , start , start1 , p , a , take ( start1 − start , elems ), take ( start1 − start , vs ))&∗& 20 [ f ] array_slice_deep(array , start1 , end , p , a , drop ( start1 − start , elems ), drop ( start1 − start , vs ));

123 21 22 lemma void array_slice_deep_split_precise(real f , T [] array , int start , int end , predicate ( A , T ; V ) p , A a , int start1 ); 23 requires [ f ] array_slice_deep(array , start , end , p , a ,? elems ,? vs )&∗& start <= start1 &∗& start1 <= end ; 24 ensures 25 [ f ] array_slice_deep(array , start , start1 , p , a , take ( start1 − start , elems ), take ( start1 − start , vs ))&∗& 26 [ f ] array_slice_deep(array , start1 , end , p , a , drop ( start1 − start , elems ), drop ( start1 − start , vs )); 27 lemma void array_slice_deep_join_precise(real f , T [] array , int start , int start1 , predicate ( A , T ; V ) p , A a , int end ); 28 requires 29 [ f ] array_slice_deep(array , start , start1 , p , a ,? elems1 ,? vs1 )&∗& 30 [ f ] array_slice_deep(array , start1 , end , p , a ,? elems2 ,? vs2 ); 31 ensures [ f ] array_slice_deep(array , start , end , p , a , append ( elems1 , elems2 ) , append ( vs1 , vs2 ));

124 B Appendix: Spec# - multithread example

In section 4.2.1 a non-concurrent Spec# example was shown. In this verification a multithreaded example is treated. The goal is to verify a simple program with multithreading, locking and shared variables. First we study the problem treated in Owicki and Gries. Consider an integer array a and a constant N ≥ 1. The task of the method FIND is to give the smallest index k ∈ 1, ...N with a[k] > 0, if such an element of a exists, otherwise k = N + 1 should hold. To add parallel computation in a later stage we split up the finding into two parts, the first part searches for an odd index k and the second for an even one. We have an additional variable i to keep track of the progress checked odd indexes and a variable oddtop to mark the end of the search. The addtop variable is also used to store the first found odd index. The first part has to increase i by two as long i < oddtop, while increasing we check for a[i] > 0 and if we find such an element we set oddtop to the value of i. This results in that we loop though array i and check for each odd index for an positieve value, and we stop when we have found it (and stored in oddtop) of we are at the end of the array. For the even indexes it is analogous.

Listing 61: SpecSharp Find example.

1 //7.4(Owicki and Gries, simplified for non −shared variable) 2 public class FIND1 3 { 4 public int k = 0 ; 5 public int N = 0 ; 6 public int topeven = 0 ; 7 public int topodd = 0 ; 8 public int [] a ; 9 public int i = 0 ; 10 public int j = 0 ; 11 12 invariant a != null ; 13 14 public FIND1 ( int []! array , int n ) 15 requires array != null ; 16 requires 0 <= n ; 17 ensures a != null ; 18 ensures 0 <= N ; 19 { 20 this . a = array ; 21 N = n ; 22 } 23 24 public int FIND () 25 requires a != null ; 26 requires 0 <= N ; 27 modifies i , topodd ; 28 modifies j , topeven ; 29 modifies k ; 30 ensures 1 <= k && k <= N + 1 ; 31 ensures forall { int l in ( 1 : k ); a [ l ] <= 0 } ; 32 ensures ( k <= N ) ==> a [ k ] > 0 ; 33 ensures k == result ; 34 ensures ( result <= N ) ==> a [ result ] > 0 ; 35 { 36 i = 1 ; 37 j = 2 ; 38 topodd = N + 1 ; 39 topeven = N + 1 ; 40 41 S1 (); 42 //assert forall { i n tl in (1: topodd);(l%2 == 1) == > a[l] <=0 } ;//holds! 43 S2 (); 44 45 //assert forall { i n tl in (1: topodd);(l%2 == 1) == > a[l] <=0 } ;

125 46 //assert forall { i n tl in (1: topeven);(l%2 == 0) == > a[l] <=0 } ; 47 //assert forall { i n tl in (1: Math.Min(topeven, topodd));a[l] <=0 } ;/ ∗ t h i s assert f a i l s, going from the two assertion above to this one is too difficult ∗/ 48 49 i f ( topeven < topodd ) 50 k = topeven ; 51 else 52 k = topodd ; 53 54 return k ; 55 } 56 57 private void S1 () 58 requires a != null ; 59 requires 0 <= N ; 60 requires i==1 && topodd == N + 1 ; 61 modifies this . 0 , i , topodd ; 62 ensures 1 <= topodd && topodd <= N + 1 ; 63 ensures forall { int l in ( 1 : topodd );( l % 2 == 1) ==> a [ l ] <= 0 } ; 64 ensures ( topodd <= N ) ==> a [ topodd ] > 0 ; 65 //ensuresa == old(a) && forall { i n tl in (1:N);a[l] == old(a[l]) } ; 66 { 67 while ( i < topodd ) 68 invariant (1 <= topodd && topodd <= N + 1) ; 69 invariant ( i % 2 == 1) ; 70 invariant (1 <= i && i <= topodd + 1) ; 71 invariant forall { int l in ( 1 : i );( l % 2 == 1) ==> a [ l ] <= 0 } ; 72 invariant ( topodd <= N ) ==> a [ topodd ] > 0 ; 73 { 74 assert i < topodd ; 75 assume i < a . Length ;/ ∗ a d d i t i o n a l assumption, we know that we are not outside the array ∗/ 76 i f ( a [ i ] > 0) 77 { 78 assert a [ i ] > 0 ; 79 topodd = i ; 80 } 81 else 82 { 83 assert a [ i ] <= 0 ; 84 i = i + 2 ; 85 } 86 } 87 assert topodd <= i ; 88 } 89 90 private void S2 () 91 requires a != null ; 92 requires 0 <= N ; 93 requires j==2; 94 requires topeven == N + 1 ; 95 modifies this . 0 , j , topeven ; 96 ensures 1 <= topeven ; 97 ensures topeven <= N + 1 ; 98 ensures forall { int l in ( 1 : topeven );( l % 2 == 0) ==> a [ l ] <= 0 } ; 99 ensures ( topeven <= N ) ==> a [ topeven ] > 0 ; 100 //ensuresa == old(a) && forall { i n tl in (1:N);a[l] == old(a[l]) } ; 101 { 102 while ( j < topeven ) 103 invariant (1 <= topeven ); 104 invariant ( topeven <= N + 1) ; 105 invariant ( j % 2 == 0) ; 106 invariant (2 <= j && j <= topeven + 1) ; 107 invariant forall { int l in ( 1 : j );( l % 2 == 0) ==> a [ l ] <= 0 } ; 108 invariant ( topeven <= N ) ==> a [ topeven ] > 0 ; 109 { 110 assert j < topeven ;

126 111 assume j < a . Length ;/ ∗ a d d i t i o n a l assumption, we know that we are not outside the array ∗/ 112 113 i f ( a [ j ] > 0) 114 { 115 assert a [ j ] > 0 ; 116 topeven = j ; 117 } 118 else 119 { 120 assert a [ j ] <= 0 ; 121 j = j + 2 ; 122 } 123 } 124 assert topeven <= j ; 125 } 126 }

In this example the postconditions of FIND can not be proven, the specification of S1 and S2 can be proven.

127