1 Introduction
2 Preliminaries
2.1 The deductive verifier KeY
x
and y
are positive, the program given in the modality (which first swaps x
and y
using arithmetics) will terminate and result in a state where \(\phi \) holds. When proving this sequent, the KeY prover will first, in a number of steps, turn the three leading assignments into explicit substitutions, apply the first to the second, the result to the third, and perform arithmetic simplification, arriving at where \((\texttt {x}\leftarrow \texttt {x+y}\,\Vert \,\texttt {y}\leftarrow \texttt {x}\,\Vert \,\texttt {x}\leftarrow \texttt {y})\) denotes the explicit (parallel) substitution resulting from symbolic execution of the first three statements. A ‘right-win’ semantics is adopted to resolve clashes in substitutions, such that the above simplifies to: In general, most proofs branch over case distinctions, often triggered by Boolean decisions in the source code. The branching happens by applying rules like the following, simplified1
if rule: In our example, applying the if rule to the latest sequent results in splitting the proof into two branches, with the following sequents, respectively:
x
being even or odd) on the state after swapping got translated into a condition on the prestate of the original program p, before the swapping. The resulting sequents tell us, among other things, that if y
is even (respectively odd) in the prestate of p, then path \(p_1\) (respectively \(p_2\)) is taken in the execution of p. In general, when building a proof in such a symbolic manner, the left side of sequents accumulate conditions on the original prestate through a particular execution path.2.2 The runtime verifier Larva
3 ppDATE: a specification language for data- and control-oriented properties
brew
starts the coffee brewing process, then it is not possible either to execute this method again, or to execute the method cleanF
(which initialises the task of cleaning the filter), until the initialised brewing process finishes.brew
is invoked and it is possible to brew a cup of coffee (i.e., the limit of coffee cups was not reached yet), then transition \(t_1\) shifts the ppDATE from state q to state \(q'\). While in \(q'\), state which represents that the coffee machine is active, if either method brew
or method cleanF
are invoked, then transitions \(t_3\) or transition \(t_4\) shift the ppDATE to state bad, respectively. This indicates that the property was violated. On the contrary, if method brew
terminates its execution, then transition \(t_2\) shifts the ppDATE from state \(q'\) to state q. Note that the names used on the transitions, e.g. \(t_1\), \(t_2\), etc, are not part of the specification language. They are included to simplify the description of how the ppDATE works.brew
is executed and its precondition holds. A similar situation stands for the property (ii) with respect to the method cleanF
. Regarding state \(q'\), the Hoare triples in this state ensure the properties: (iii) no coffee cups are brewed; (iv) filters are not cleaned. Property (iii) and (iv) are verified if either method brew
and method cleanF
are executed, and their preconditions hold, respectively. Here, remember that this state represents that the coffee machine is active. Thus, if it occurs that either the method brew
or the method cleanF
are executed while the ppDATE is on this state, then, as this would move the ppDATE to state bad
, one would expect the value of the variable cup
to remain unchanged. This is precisely what is verified when either property (iii) or (iv) are analysed.login
), she can only perform 10 file transfers (transferFile
) before logging out (logout
). This fact is tracked using the ppDATE variable c. This variable keeps count of the number of files transferred in a single session. Whenever a user logs in, the ppDATE moves to state \(q'\) and c is set to 0 (zero). While in \(q'\), this variable is increased by one every time a file transfer is performed. If at some point the user transfers a file but the value of c is bigger than 10, then the ppDATE moves to state bad, i.e., the property was violated.4 The StaRVOOrS framework
brew()
is given by:
active
. In general, the missing information can be an arbitrary condition on the system state, more than just a Boolean as is the case here.5 Formal definition of ppDATEs
5.1 Notation
5.2 ppDATE
-
its execution always terminates,
-
the method calls on its body do not generate any observable system event,
-
it does not interfere with the system under scrutiny, i.e., it does not modify the values of system variables. \(\square \)
m.get(k) == o
’). Additional features of Boolean JML expressions include universal and existential quantification, which are frequently used in Hoare triples, the ability to refer in a postcondition to a) the return value (with ‘
’), and b) the preexecution value of an expression (like in ‘
’).-
any side-effect free Boolean Java expression is a BJMLE,
-
if
a
andb
are BJMLEs, andx
is a variable of typet
, the following expressions are BJMLEs:-
!a
,a&&b
, anda||b
-
\(\texttt {a ==}> \texttt {b}\) (“
a
impliesb
”) -
\(\texttt {a} <\texttt {==}> \texttt {b}\) (“
a
is equivalent tob
”) -
(“for all
x
of typet
,a
holds”)× -
(“there exists
x
of typet
such thata
”)× -
(“for all
x
of typet
fulfillinga
,b
holds”)× -
(“there exists an
x
of typet
fulfillinga
, such thatb
”)×
-
-
replacing any sub-expression
e
in a BJMLE with gives a BJMLE, -
replacing any sub-expression in a BJMLE with gives a BJMLE, (well-typedness is context dependent, see Definition 5) \(\square \)
a; b)
”, they relate to standard quantification in exactly the same way as was explained in Sect. 5.1. (The only difference is that there we discussed meta-level notation, whereas BJMLE is part of ppDATE.) The constructs
and
are only allowed in postconditions of Hoare-triples (i.e., in \(\pi '\)).
refers to the return value of a (non-void
) method.
allows to evaluate sub-expressions not in the post-state (which is the default), but in the prestate of a method’s execution. For instance, ‘
’ in a postcondition of method m
says that the difference between the values of x
before and after the execution of m
is the value which y
had before
m
’s execution.-
Q is the finite set of states.
-
t is the transition relation among states in Q, where each transition is tagged with (i) a trigger; (ii) a condition; (iii) an action which may change the valuation of ppDATE variables: \(t \subseteq Q \times trigger \times cond_{\textit{Sys}\cup V} \times action \times Q\).
-
\(B \subseteq Q\) is the set of bad states.
-
\(q_0 \in Q\) is the initial state.
-
\(\varPi \) is a function which tags each state of m with Hoare triples for particular method names in \(\varSigma \): \(\varPi \in Q \longrightarrow {\mathcal P}(cond_{\textit{Sys}} \times \varSigma \times postcond_{\textit{Sys}})\).\(\square \)
-
\(Q = \{ q, q', \texttt {bad } \}\),
-
\(V = \{ c \}\),
-
\( \varSigma = \{ \texttt {fileTransfer},\;\texttt {login},\;\texttt {logout} \}\),
-
\(B = \{ \texttt {bad } \}\),
-
\(q_0 = q.\)
-
The set of ppDATE templates of order 0 is exactly the set of ppDATEs.
-
Assume C is a syntactic sub-category of ppDATE (Definition 6), i.e., a syntactic (sub-)category of \(Q,t,B,q_0,\) or \(\varPi \), respectively. If m is a ppDATE template of order n, then \(\lambda X\!\!:\!\!C.m'\) is a ppDATE template of order \(n+1\), where \(m'\) is the result of replacing, in m, some (sub-)term trm of category C by X. We call X the template variable of \(\lambda X\!\!:\!\!C.m'\). \(\square \)
-
M is a set of ppDATEs. If \(m \in M\), then we say that m = \((Q_m,t_m,B_m,q_{0m},\varPi _m)\).
-
\(V\) is a set of ppDATE variables.
-
\(T_{ppd}\) is a set of ppDATE templates.\(\square \)
6 ppDATE semantics
6.1 Events, valuations, and traces
x
. For \(c \in postcond_{\textit{Sys}}\), we define \(\theta , \theta ' \models c\)
iff
\({eval}_{\theta , \theta '}(c) = T\). The definition of \({eval}_{\theta , \theta '}\) is almost identical to the definition \({eval}_\beta \), with the base case \({eval}_{\theta , \theta '}(\texttt {x}) = \theta '(\texttt {x})\) for program variables x
. The only case in the definition where the pre-valuation \(\theta \) matters is the evaluation of
-expressions:
. This means that, in postconditions, the post-valuation \(\theta '\) acts as the default, however not inside
-expressions, where instead the pre-valuation \(\theta \) counts. The other additional operator in postconditions is
. To handle its evaluation properly, we assume a special system variable named
. Whenever a non-void method returns, its return value, say val, is assigned to
, such that, in the post-valuation \(\theta '\), we have
.6.2 Configurations
-
is a set of local configurations. For each \(m \in M\), there is exactly one q and one \(\rho \), such that \((m,q,\rho ) \in L\). For each \((m,q,\rho ) \in L\), we have \(q \in Q_m\) and either \(m \in M\) or \(m = {inst}(t,\ \overline{{args}})\), for some \(t \in T_{ppd}\).
-
\(\nu \) is ppDATE variable valuation with domain \(V\).\(\square \)
-
is a global configuration for \(pn\),
-
\(E \subseteq {\mathcal P}(\xi )\) is a set of events,
-
\(\theta \in \varTheta _{\textit{Sys}}\) is a system variables valuation. \(\square \)
6.3 Semantics of actions
-
\(\nu ' \in N\) is a ppDATE variable valuation computed (locally) in a,
-
\(W \subseteq V\) is a set of ppDATE variables written to in a,
-
\(R \subseteq V\) is a set of ppDATE variables read from in a,
-
\(E \subseteq \textsf {actevent}\) is a set of action events generated in a,
-
\({New} \subseteq \textit{ppDATE} \) is a set of ppDATEs newly created in a.
6.4 Structural operational semantics
6.4.1 Auxiliary predicates
6.4.2 Small steps for local configurations
6.4.3 Small steps for extended global configurations
6.4.4 Big steps for global configurations
6.5 Valid traces and violating traces
cleanF
terminates, then
is a counter-example corresponding to the case (ii) of Definition 22. \(\square \)
7 From ppDATE to DATE
7.1 DATE
7.2 Translation from ppDATEs to DATEs
-
\(h_1\):
-
\(h_2\):
-
\(h_3\):
-
\(h_4\):
brew()
is executed and the precondition of \(h_1\) holds, then its postcondition will have to be verified whenever method brew()
finishes its execution.-
\(a_1\!:\)
-
\(a_2\!:\) ;
-
\(a_3\!:\) .
part_eval
partially evaluates its argument, replacing the expressions
operator the current value of e. If a postcondition does not include such operator, then part_eval
is the identity. Note that even though the if-expression in the transitions \(t_1'\) and \(t_4'\) may seem unnecessary, we include it anyway in order to exactly reflect how the translation algorithm works.
-
\(a_4\!:\)×
-
\(t_2': \)
7.2.1 Translation algorithm
part_eval
\(\in {postcond} \mapsto {cond}\), which removes
constructs in postconditions. The function performs partial evaluation—replacing each
with the current value of e. Our algorithm syntactically places the part_eval
function in an action that will be executed when the according method is entered, i.e., partial evaluation does not happen during the translation algorithm, but at runtime, when the method is entered.ret
. This variable will represent the value returned by the method associated to the Hoare triple/ makes its own instance of this variable).-
3\(a_1\). Each non-clashing Hoare triple: in \(\varPi _m(q)\) is turned into a transition×
-
3\(a_2\). For each clashing Hoare triple: \(\in \varPi (q)\), clashing with n outgoing transitions, (\(0 \le k < n\)):where, in both cases, \(a = \textsf {create}(exit\_cond\_checker,\sigma ,\textit{part}\_\textit{eval}(\pi '))\)
-
Replace with: ;××
-
Add the following transition: ,×
× -
-
3b. each transition such that either \(\varPi _{m}(q) = \emptyset \), \(\varPi _{m}(q) \ne \emptyset \) but there is no Hoare triple associated to trigger tr, or trigger tr is activated by an exit event, remains unchanged, i.e. .
7.3 Proof of soundness of the translation algorithm
7.4 Coupling invariant lemmas
7.4.1 Proof of soundness
8 The StaRVOOrS tool implementation
Hashtable
object, whose class represents an open addressing hashtable with linear probing as collision resolution. Method add
, which is used to add objects into the hashtable, first attempts to put the corresponding object at the position of its computed hash code. However, if that index is occupied, then add
searches for the nearest following index which is free. Figure 12 depicts a code snippet for this method. Within the hashtable object, users are stored into an array arr
. This means that the set of logged-in users has its capacity limited by the length of arr
. In order to check in a straightforward manner whether the capacity of arr
is reached or not, a field size
keeps track of the amount of stored objects and a field capacity
represents the (total) number of objects that can be added into the hash table. In addition, this system has to fulfil the properties described with the ppDATE template depicted in Fig. 13. This template specifies the following properties:add
with object
o
and key
k
should add the object to the table.
add
places the object o
in the hashtable, there exists an index in the array arr
such that o
is placed in that index, i.e., . Note that the given Hoare triple is only included in state \(q_1\) since only a successful login leads to the execution of the method add
, i.e., this Hoare triple is context dependent; and that
means that method login
associated to the trigger is the one defined within object f. In addition, we assume that the specification of the system has a ppDATE with a single state q and single transition of the form
, such that the trigger
is activated by the declaration of an object o of the class UserInterface
. Thus, this ppDATE creates an instance of the template in Fig. 13 every time an object of the class UserInterface
is declared.
8.1 ppDATE specification as an input script for StaRVOOrS
IMPORTS
lists the Java packages which may be used in any of the other sections of the script, in this case UserInterface
and Hashtable
. The section TEMPLATES
contains the description of the ppDATE templates (tagged by TEMPLATE
). Here, the section TRIGGERS
is used to declare the different triggers which may be used in the transitions of the ppDATE, i.e, login_exit
, logout_entry
, deposit_entry
, and the section PROPERTY
describes the different states, i.e., q1
, q2
and bad
, and transitions of the ppDATE. Note that the syntax q1 (add_ok)
associates the Hoare triple tagged as add_ok
to state q1
. This means that the Hoare triple add_ok
has to be verified if the method associated to it, in this case method add
, is executed whenever the ppDATE is in state q1
. The section GLOBAL
contains the description of the ppDATE. Here, ppDATEs are described in the same manner as in a TEMPLATE
section. However, note that it is also possible, as it is the case in our example, to use the special section PINIT
when describing the section PROPERTY
. Section PINIT
represents a ppDATE with single state, and a looping transition which is fired every time an object of the class listed within this section (UserInterface
in our example) is declared, leading to the creation of an instance of the listed template for that object (prop-deposit-temp in our example). We have included this special case because it is quite common to have ppDATEs only focus on creating instances of a template upon declaration of a particular object. Regarding the section CINVARIANTS
, class invariants are described by the syntax class_name {invariant}
, meaning that invariant
has to be fulfilled by all the methods in the class class_name
. These invariants are only meant as a help for the deductive verification of the Hoare triples (see Sect. 8.2). If no invariants are needed, then this section can be omitted. Finally, the section HTRIPLES
gives a list of named Hoare triples (tagged by HT
). Here, PRE
describes the precondition of the Hoare triple, POST
describes the postcondition of the Hoare triple, METHOD
indicates which one is the method associated to the Hoare triple, and ASSIGNABLE
lists the (class) variables that might be modified when the method associated to the Hoare triple is executed. Note that the predicates in invariants, pre- and postconditions follows JML-like syntax and pragmatics. For instance, in the Hoare triple add
_ok
the second semicolon separates the range predicate (\(\texttt {i}>\texttt {=0}\)&& \(\texttt {i}<\texttt {capacity}\)) from the desired property over integers in that range, (arr[i]==o
).8.2 Running StaRVOOrS
8.2.1 Deductive verification
add
is extracted from the Hoare triple add
_ok
:
requires
clause describes the precondition of add
, the ensures
clause describes the postcondition of add
, and the assignable
clause lists the (class) variables that might be modified when add
is executed.add
, there was a branching in the condition arr[hash_function(key)] = null
, leading to two possible execution paths (depending on its truth value). Recalling the code snippet in Fig. 12, this condition corresponds to the condition on the if-expression in line 4. Thus, the execution path for the condition arr[hash_function(key)] = null
corresponds to the case where the array arr
has a free slot at the hash code of key
, whereas the execution path for the condition !arr[hash_function(key)] = null
corresponds to the case where the program enters the while-loop in line 10, searching for the next free slot in arr
. In addition, in the XML, the component verified
represents whether KeY was able to prove the branch of the proof (verified=true
), or not (verified=false
). Therefore, from the previous fragment of the XML file we know that KeY was able to close the branch where the array arr
has a free slot (= null
) at the hash code of key
, but it was not able to verify the other case (where the program enters a loop searching for the next free slot). The main reason why KeY was not able to prove the latter case is the lack of loop invariants to deal with the while-loop.8.2.2 Specification refinement
add_ok
will be refined with the condition for the one goal not closed by KeY, i.e., !(arr[hash_function(key)] == null)
. The Hoare triple will thus be strengthened as follows:
8.2.3 Translation and instrumentation
8.2.4 Pre/postcondition operationalisation
add_ok
has an existential quantifier, it has to be operationalised, producing the following method:
arr[i]==o
. Thus, if any value on the range of the existential quantification fulfils its body, then this method returns true
, i.e., exists a value that fulfils the existential quantification. Otherwise, it returns false
, i.e., it does not exist a value fulfilling the existential quantification.8.2.5 Code instrumentation
add
has to be instrumented, resulting in:
addAux
implementation corresponds to the body of method add
in Fig. 12. This method represents the instrumentation of method add
with the extra argument Integer id
, which is used as identifier. In addition, method add
now simply calls addAux
, but generating a fresh identifier for the call using function fid.getNewId
.addAux
instead of method add
.8.2.6 Monitor generation
9 Case study: SoftSlate Commerce
9.1 ppDATE specification
9.1.1 Login–logout
User
is created, and the ppDATE template login–logout (Fig. 15) describes the following properties:prepareCheckout
, should return “success”. Regarding property (v), a user is only considered to be a costumer if she has logged-in into the application. Even though this property seems to be similar to property (i), this similarity is only apparent. Property (i) only addresses the proper order in which the methods should be executed, whereas property (v) focuses on controlling how the data related to a user is modified during such executions. Finally, both properties (iv) and (v) are only placed in state login because that is the only state in which a successful purchase can occur, i.e., (iv) and (v) are context dependent data-oriented properties.9.1.2 Purchases checkout
User
is created, and the ppDATE template prop-checkout (Fig. 17) describes the following properties:9.2 Using StaRVOOrS
9.2.1 Login–logout
9.2.2 Purchases checkout
updateOrderAndDeliveryTotals
only if the condition user.getOrder() != null
holds. Thereby, this property is refined by StaRVOOrS as follows:
9.3 Experimentation
9.3.1 Properties analysis
9.3.2 Login–logout
User
was associated to a session, whose information was unique for each different execution of the application. Though the relation between (real) users and the session is bijective (for each real user there is a unique session, and vice versa), there were (at least) two instances of the class User
, \(u_{logged}\) and \(u_{new}\), associated with each session.9.3.3 Purchases checkout
9.3.4 Runtime verification overhead analysis
Purchases | (a) No monitoring (ms) | (b) Monitoring (ms) \({{\varvec{\mathsf{{ S}}}}}\)
| (c) Monitoring \({{\varvec{\mathsf{{ S}}}}}'\) (ms) |
---|---|---|---|
1 | 800 | 1300 | 1100 |
10 | 10, 500 | 15, 500 | 13, 000 |
100 | 120, 000 | 190, 000 | 150, 000 |
10 Case study: Mondex
10.1 ppDATE property
val_operation
which transfers money from the source purse to the destination one should succeed and increase the money of the destination purse by the sent amount (provided the limit of its account has not been reached), as shown in the Hoare triple below:
val_operation
.10.2 Using StaRVOOrS
val
_operation
, which we will refer here to as val_operation_ok:
status != 2
holds upon entering val
_operation
(i.e., the destination purse is not waiting for the arrival of the requested money). Thus, the previous Hoare triple was refined by StaRVOOrS as follows:
10.3 Experimentation
10.3.1 Normal behaviour
Transactions | (a) No monitoring (ms) | (b) Monitoring \({{\varvec{\mathsf{{ S}}}}}\) (ms) | (c) Monitoring \({{\varvec{\mathsf{{ S}}}}}'\) (ms) |
---|---|---|---|
10 | 8 | 120 | 15 |
100 | 50 | 3500 | 90 |
1000 | 250 | 330, 000 | 375 |
10.3.2 Faulty behaviour
val_operation
—the value of variable balance
is incremented with a different amount from the one given in the specification of the method. When analysing property val_operation_ok
, KeY obviously does not manage to prove it. Therefore, the whole property will have to be runtime verified. The monitor spots this error reaching a bad state.ConPurse
class constructor, and there is an implementation error where the variable balance
is initialised to \(-1\) instead of being initialised to 0. In spite of the error in the specification, KeY would proceed normally with the proofs and the previous particular situation would not be directly controlled on runtime. However, this erroneous initialisation leads to an erroneous initial charge of money in the purses (performed using the method chargeMoney
in class ConPurse
). As balance
is negative, the previous method fails to update it with the new amount of money. Hence, after applying chargeMoney
the value of balance
is still \(-1\). Thereby, whenever a purse tries to begin a transfer, either the method initialising the sender purse during a transaction or the method initialising the receiver purse during a transaction will fail its execution (the former due to insufficient funds and the latter due to a value overflow). This failure leads to an unsuccessful termination of the transfer, which is detected by the monitor controlling the transaction protocol and takes it to a bad state. This analysis can be easily conclude by inspecting the execution trace generated by the monitor. This trace allows one to backtrack through the execution of the different methods until reaching which was the problem which was the cause the failure. In this scenario, it is important to note that in spite of the fact that we have not enforced any Hoare triple on the constructor of class ConPurse
, it was specified and proved correct using KeY.