{'fraction_non_alphanumeric': 0.04093402728935153, 'fraction_numerical': 0.006932861764764986, 'mean_word_length': 4.312126387702818, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 0, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
Automatically Verifying Temporal Properties of Pointer Programs with Cyclic Proof

In this article, we investigate the automated verification of temporal properties of heap-aware programs. We propose a deductive reasoning approach based on cyclic proof. Judgements in our proof system assert that a program has a certain temporal property over memory state assertions, written in separation logic with user-defined inductive predicates, while the proof rules of the system unfold temporal modalities and predicate definitions as well as symbolically executing programs. Cyclic proofs in our system are, as usual, finite proof graphs subject to a natural, decidable soundness condition, encoding a form of proof by infinite descent. We present a proof system tailored to proving CTL properties of nondeterministic pointer programs, and then adapt this system to handle fair execution conditions. We show both versions of the system to be sound, and provide an implementation of each in the Cyclist theorem prover, yielding an automated tool that is capable of automatically discovering proofs of (fair) temporal properties of pointer programs. Experimental evaluation of our tool indicates that our approach is viable, and offers an interesting alternative to traditional model checking techniques.

overapproximates all possible executions of the program, and then checks that the desired temporal property holds for this model (see e.g. [10,13,15]). However, this approach has been applied mainly to integer programs; the situation for memory-aware programs over heap data structures becomes significantly more challenging, mainly because of the difficulties in constructing suitable abstract models. One possible approach is simply to translate such heap-aware programs into integer programs, in such a way that properties such as memory safety or termination of the original program follow from corresponding properties in its integer translation [13,15,22]. However, for more general temporal properties, this technique can produce unsound results. In general, it is not clear whether it is feasible to provide suitable translations from heap to integer programs for any given temporal property; in particular, numerical abstraction of heap programs often removes important information about the exact shape of heap data structures, which might be needed to prove some temporal properties.
Example 1 Consider a "server" program that, given an acyclic linked list with head pointer x, nondeterministically alternates between adding an arbitrary number of "job requests" to the head of the list and removing all requests in the list: Memory safety of this program can be proven using a simple numeric abstraction recording emptiness/nonemptiness of the list. Proving instead that throughout the program execution it is always possible for the heap to become empty, expressed in CTL as AG E F(emp), requires a finer abstraction, recording the length of the list. However, such an abstraction is still not sufficient to prove shape analysis properties of the heap, say that the heap is always a nilterminating acyclic list from x, expressed in CTL as AG(ls(x, nil)) (where ls is the standard list segment predicate of separation logic [26]), because the information about acyclicity and head/tail pointer values is lost, begging the question: how to differentiate cyclic from acyclic lists using only their length?
Thus, although it is often possible to provide numeric abstractions to suit specific programs and temporal properties, it is not clear that this is so for arbitrary programs and properties.
In this article, we instead approach the above problem via the main (perhaps less fashionable) alternative to model checking, namely the direct deductive verification. Common limitations of previous temporal logic proof systems include restriction to finite state transition systems [4,18,19] and/or a reliance on complex verification conditions that are seemingly difficult to fully automate [23,29], the latter being arguably the most cited argument against deductive verification. In contrast, our proof system can handle infinite state systems, and an automated implementation is directly derivable from the proof rules and automata-based soundness checks, showing that temporal logic proof systems can indeed work in practice.
Our proof system manipulates temporal judgements about programs, and employs automatic proof search in this system to verify that a program has a given temporal property. Given some fixed program, the judgements of our system assert a temporal property of the program whose initial state satisfies some precondition, written in a fragment of separation logic [26], a well-known language for describing heap memory; in particular we also permit user-defined (inductive) predicates. The core of the proof system is a set of logical rules that operate on common logical connectives and unfold temporal modalities and predicate definitions, along with a set of symbolic execution rules that simulate program execution steps.
To handle the fact that symbolic execution can in general be applied ad infinitum, we employ cyclic proof [6,7,9,30], in which proofs are finite cyclic graphs subject to a global soundness condition. Using this approach, we are frequently able to verify temporal properties of heap programs in an automatic and sound way without the need of numeric abstractions or program translations. Moreover, our analysis has the added benefit of producing independently checkable proof objects.
Our proof system is tailored to proving standard CTL program properties over separation logic assertions; subsequently, we show how to adapt this system to handle fairness constraints, where nondeterministic branching may not unfairly favour one branch over another. We have also adapted our system to (fair) LTL properties, though we do not present this adaptation in this paper due to space constraints. The details of our LTL cyclic proof systems will appear in the first author's forthcoming PhD thesis.
We provide an implementation of our proof system as an automated verification tool within the Cyclist theorem proving framework [9] and evaluate its performance on a range of examples. The source code, benchmark and executable binaries of the implementation are publicly available online [1]. Our tool is able to discover surprisingly complex cyclic proofs of temporal program properties, with times often in the millisecond range. Practically speaking, the advantages and disadvantages of our approach are entirely typical of deductive verification: on the one hand, we do not need to employ abstraction or program translation, and we guarantee soundness; on the other hand, our algorithms might fail to terminate, and (at least currently) we do not provide counterexamples in case of failure. Thus we believe our approach should be understood as a useful complement to, rather than a replacement for, model checking.
The remainder of this paper is structured as follows. Section 2 introduces our programming language, the memory state assertion language, and temporal (CTL) assertions over these. Section 3 introduces our proof system for verifying temporal properties of programs, and Sect. 4 modifies this system to account for fair program executions. Section 5 presents our implementation and experimental evaluation, Sect. 6 discusses related work and Sect. 7 concludes. This is an expanded journal version of our previous conference paper [31]. In the present paper we have endeavoured to include as much of the technical detail of our development (particularly our soundness proofs) as space permits.

Programs and Assertions
In this section we introduce our programming language, our language of assertions about memory states (based on a fragment of separation logic) and our language for expressing temporal properties of programs, given by CTL over memory assertions. Programming language We use a simple language of while programs with pointers and (de)allocation, but without procedures. We assume a countably infinite set Var of variables and a first-order language of expressions over Var. Branching conditions B and commands C are given by the following grammar: A (program) configuration γ is a triple C, s, h where C is a command, s a stack and h a heap. If γ is a configuration, we write γ C , γ s , and γ h respectively for its first, second and third components. A configuration γ is called final if γ C = skip.
The small-step operational semantics of programs is given by a binary relation on program configurations, where γ γ holds if the execution of the command γ C in the state (γ s , γ h ) can result in a new program configuration γ . We write * for the reflexive-transitive closure of . The special configuration fault is used to denote a memory fault, which results when a command tries to access non-allocated memory. The operational semantics of our programming language is shown in Fig. 1.
An execution path is a (maximally finite or infinite) sequence (γ i ) i≥0 of configurations such that γ i γ i+1 for all i ≥ 0. If π is a path, then we write π i for the ith element of π. A path π starts from configuration γ if π 0 = γ .

Remark 1
In temporal program verification, it is common to consider all program execution paths to be infinite, and all temporal operators to quantify over infinite paths. This can be achieved either (i) by modifying programs to contain an infinite loop at every exit point, or (ii) by modifying the operational semantics so that final configurations loop infinitely (i.e. skip, s, h skip, s, h ). Here, instead, our temporal assertions quantify over paths that are either infinite or else maximally finite. This has the same effect as directly modifying programs or their operational semantics, but seems to us slightly cleaner.
An execution tree, arising from the nondeterministic and branching nature of program executions, is a directed rooted tree in which any two vertices are connected by an execution path. Memory state assertions We express properties of memory states (s, h) using a standard symbolic-heap fragment of separation logic (cf. [2]) extended with user-defined (inductive) predicates, typically used to express data structures in the memory.
We assume a fixed first-order logic language Σ log that extends the expression language of our programming language (i.e. Σ log ⊇ Σ exp ). The terms of Σ log are defined as usual, with variables drawn from Var. We write t(x 1 , . . . , x k ) for a term t all of whose variables occur in {x 1 , . . . , x k } and we use vector notation to abbreviate sequences. The interpretation t s of a term t of Σ log in a stack s is then defined in the same way as expressions, provided we are given an interpretation for any constant or function symbol that is not in Σ exp .
Definition 1 A symbolic heap is given by a disjunction of assertions each of the form Π : Σ, where Π is a finite set of pure formulas of the form E = E or E = E, and Σ is a spatial formula given by the following grammar: where E ranges over expressions, E over tuples of expressions and Ψ over predicate symbols (of arity matching the length of E in Ψ (E)). A symbolic heap singleton is a symbolic heap composed of a single assertion.

Definition 2
The satisfaction relation s, h | φ between program states (s, h) and pure or spatial formulas φ is given inductively as follows: where the definition of Ψ Φ , the interpretation of inductive predicate Ψ according to inductive definition(s) Φ, is given below. We extend satisfaction conjunctively from pure formulas to sets of pure formulas, and then to full symbolic heaps by s, h | Π 1 : Σ 1 ∨ · · · ∨ Π n : Σ n ⇔ 1≤i≤n s, h | Π i and s, h | Σ i .
As usual in separation logic, the separating conjunction * is easily seen by the above definition to be associative and commutative, with unit emp. In the remainder of this paper, we take these laws for granted.
It remains to define the interpretation Ψ of each inductive predicate symbol Ψ , which is determined by a given inductive definition for Ψ . Our inductive definition schema follows closely the one formulated in e.g. [6,8].

Definition 3 (Inductive definition)
An inductive definition of an inductive predicate symbol Ψ is a finite set of inductive rules, each of the form Π : Σ ⇒ Ψ (E), where Π : Σ is a symbolic heap singleton and Ψ (E) is a predicate formula.
The standard interpretation of all inductive predicate symbols Ψ 1 , . . . , Ψ n is then the least prefixed point of a mutual monotone operator constructed from the inductive definitions of all inductive predicate symbols.
Definition 4 (Interpretation of inductive predicates) Let inductive definitions of predicate symbols = (Ψ 1 , . . . , Ψ n ), with arities a 1 , . . . , a n respectively, be given. We may write the inductive definition for any such symbol, say Ψ i , in the form We define a corresponding n-ary function χ i by where X = (X 1 , . . . , X n ) with each X i ⊆ Val a i × Heaps and s is an arbitrary stack and | X is exactly the satisfaction relation given above, except that Ψ i Example 2 Consider the following inductive definition for a binary inductive predicate symbol ls denoting singly-linked list segments: Then ls is the least (pre)fixed point of the following operator, which has domain and codomain Pow(Val 2 × Heaps): We note that the operators χ i in Definition 4 are all monotone [6] and consequently the least prefixed point Ψ Φ indeed exists. Moreover, this least prefixed point can be iteratively approached in approximant stages.

Definition 5 (Approximants) Let Ψ Φ be as given in Definition 4, and write
It is a standard fact that, for some ordinal α, the prefixed point Ψ Φ is equal to the approximant Ψ α ); in [6] it is shown that for our definition schema the closure ordinal is at most ω. Temporal assertions We describe temporal properties of our programs using temporal assertions, built from the memory state assertions given above using standard operators of computation tree logic (CTL) [11], where the temporal operators quantify over execution paths from a given configuration. Definition 6 CTL assertions are described by the following grammar: where P ranges over symbolic heaps (Definition. 1).
Note that final and error denote final, respectively faulting configurations.

Definition 7
A configuration γ is a model of the CTL assertion ϕ if the relation γ | ϕ holds, defined by structural induction on ϕ as follows: Judgements in our system are given by P C : ϕ, where P is a symbolic heap, C is a command sequence and ϕ is a CTL assertion.

Definition 8 (Validity)
A CTL judgement P C : ϕ is valid if and only if, for all memory states (s, h) such that s, h | P, we have C, s, h | ϕ. Proof rules for CTL judgements. We write ϕ to mean "either ϕ or ♦ϕ". Note that primed variables x are always chosen fresh standard Hoare logic, appealing to entailment | between symbolic heaps, and between CTL assertions. In particular, the (Unfold-Pre) rule performs a case-split on an inductive predicate in the precondition by replacing the predicate with the body of each clause of its inductive definition. For example, the inductive definition of the linked list segment predicate from Example 2 determines the following (Unfold-Pre) rule: Proofs in our system are cyclic proofs: standard derivation trees in which open subgoals can be closed either by applying an axiom or by forming a back-link to an identical interior node. To ensure that such structures correspond to sound proofs, a global soundness condition is imposed. The following definitions, adaptations of similar notions in e.g. [6][7][8][9]27], formalise this notion. A pre-proof P = (D, L) can be seen as a finite cyclic graph by identifying each open leaf of D with its companion. A path in P is then simply a path in this graph with no further implications. It is easy to see that a path in a pre-proof corresponds to one or more paths in the execution of a program, interleaved with logical inferences.
To qualify as a proof, a cyclic pre-proof must satisfy a global soundness condition, defined using the notion of a trace along a path in a pre-proof.
The sequence of temporal formulas along the path, (ϕ i ) i≥0 , is a -trace (♦-trace) following that path if there exists a formula ψ such that, for all i ≥ 0: -the formula ϕ i is of the form AGψ (EGψ) or AGψ (♦EGψ); and ϕ i+1 = ϕ i whenever J i is the conclusion of (Cons).
We say that a trace progresses whenever a symbolic execution rule is applied. A trace is infinitely progressing if it progresses at infinitely many points.
We remark that CTL formulas with an outermost AF/E F quantifier never form part of a trace. Such properties, which express that something will eventually happen, are typically proven by appealing to an infinite descent in the precondition. To this end, we also introduce precondition traces as employed in [7]. Roughly speaking, a precondition trace tracks an occurrence of an inductive predicate in the preconditions of the judgements along the path, progressing whenever the predicate occurrence is unfolded.
We say that a precondition trace progresses whenever (Unfold-Pre) is applied. A precondition trace is infinitely progressing if it progresses at infinitely many points.

Definition 12 (Cyclic proof)
A pre-proof P is a cyclic proof if it satisfies the following global soundness condition: for every infinite path (P i C i : ϕ i ) i≥0 in P, there is an infinitely progressing -trace, ♦-trace or precondition trace following some tail (P i C i : ϕ i ) i≥n of the path.
While admittedly not the most interesting program in itself, the following simple example has been designed to illustrate complex cycle structures that arise from the nesting of temporal operators to provide a non-trivial satisfaction of the soundness condition.
Example 3 Consider the following program, where each atomic command is labelled with a program counter: 1 : if (*) then 2 : x :=1; else 3 : skip ; fi ; 4 : while ( x = x ) do 5 : skip ; od ; One can observe that, under the precondition P = (x = 1), the program has the invariant property AG(x = 1), since the assignment command on line 2 does not break the invariant and the variable will not be updated throughout the rest of the program execution. Moreover, since there exists at least one program execution in which the invariant holds, the program satisfies the formula EG AG(x = 1). Figure 3 shows the proof of this property in our system, including the six cycles that are formed during the proof search, with traces that follow the infinite paths. For the rightmost cycle in the lower branch of the figure we note that the temporal components of the sequents in the infinite proof path are all of the form EGψ or ♦EGψ, where ψ = AG(x = 1), so that there is a ♦-trace following the proof path as per Definition 10. Moreover, due to the application of symbolic execution rules (Wh) and (Skip) along the infinite proof path, the trace progresses infinitely often. Similarly, for the leftmost cycle in the top branch of the figure, we note that the temporal components of the sequents in the infinite proof path are of the form AGψ or AGψ, where ψ = (x = 1), so that there is a -trace following the proof path. Moreover, due to the application of symbolic execution rules (Wh) and (Skip) along the infinite proof path, the trace progresses infinitely often. Contrary to the previous two cycles, the remaining 4 back-links shown in the proof do not match their corresponding leaf node to a direct ancestor. Nevertheless, these infinite paths are, too, followed by -traces that progress infinitely often. Consequently, our pre-proof qualifies as a valid cyclic proof.
For a more realistic example, we now present a proof of a heap-aware server program that nondeterministically alternates between adding an arbitrary number of "job requests" to the head of a linked-list and processing job requests by means of deleting them from the list.

Example 4
Consider the following server-like program C from Example 1 in the Introduction, restated here with line numbers for convenience: 1 : while ( x = x ) do 2 : if (*) then 3 : while x != nil do 4 : temp :=[ x ]; free ( x ) ; x := temp ; od ; else 5 : while (*) do 6 : y := new () ; [ y ]:= x ; x := y ; od ; fi ; od ; First we show that, given that the heap is initially a linked list from x to nil, it is possible for the heap to become empty when running the program. Writing C i for the program from line i, this property is expressed as the judgement ls(x, nil) C 1 : E F(emp). We show a cyclic proof of this property in Fig. 4, relying on a precondition trace over ls(x, nil), which is unfolded during the proof.
Using this proof as a lemma, we can then show that it is always possible for the heap to become empty at any point during program execution: ls(x, nil) C 1 : AG E F(emp). Essentially, given the above lemma, the remaining proof burden consists in showing that line 1 is reachable from any other program point. Figure 5 shows an outline cyclic proof of this judgement in our system (we suppress the internal judgements for space reasons, but show the cycle structure and rule applications). Note that the back-links depicted in blue all point to a companion in the proof of our auxiliary lemma, and do not yield any new infinite paths. The red back-links do give rise to new infinite paths; one can see that the pre-proof qualifies as a valid cyclic proof since there is an infinitely progressing -trace along every one of these paths.

Proposition 1 (Decidable soundness condition) It is decidable whether a pre-proof is a cyclic proof.
Proof (Sketch) Given a pre-proof P, we construct two Büchi automata over strings of nodes of P. The first automaton B 1 accepts all infinite paths of P. The second automaton B 2 accepts all infinite paths of P such that an infinitely progressing -, ♦or precondition-trace exists on some tail of the path. We then simply check L(B 1 ) ⊆ L(B 2 ), which is decidable.
We now show that our proof system is sound. Therefore length(π ) ≤ length(π). Moreover, length(π ) < length(π) when R is a symbolic execution rule.
2. if there is a ♦-trace following the edge (J , J ) then, letting ψ be the subformula of ϕ given by Definition 10, there is a smallest finite execution tree κ with root C, s, h , each of whose leaves γ satisfies γ | ψ. Moreover, κ has a subtree κ with root C , s , h and whose leaves are all leaves of κ. Therefore height(κ ) ≤ height(κ). Moreover, height(κ ) < height(κ) when R is a symbolic execution rule. 3 Proof We distinguish a case for each proof rule. Due to space constraints, we show only a few interesting cases: the symbolic execution rules (If*♦1) and (If* ), which provide progress for ♦and -traces respectively, and the unfolding rule (Unfold-Pre), which provides progress for precondition traces. Case (If*♦1) Assume the conclusion of the rule is invalid. Pick an arbitrary program state such that (s, h) | P but γ = if * then C 1 else C 2 fi ; C 3 , s, h | ♦ϕ. By Definition 7, if γ | ♦ϕ then for all γ such that γ γ , γ | ϕ. By the operational semantics of our programming language we know that there are two possible transitions: γ C 1 ; C 3 , s, h and γ C 2 ; C 3 , s, h . Hence, C 1 ; C 3 , s, h | ϕ and C 2 ; C 3 , s, h | ϕ. Consequently, since by our assumption (s, h) | P but C 1 ; C 3 , s, h | ϕ then the premise is invalid.
Given the structure of the temporal formula ϕ/♦ϕ, there cannot be a -trace following the edge.
If there is a ♦-trace following the edge, then by Definition 10, ϕ = EGψ. Since γ | EGψ, by Definition 7 we know that for all paths π starting from γ there existsγ ∈ π such thatγ | ψ. In other words, there is a finite execution tree κ with root γ and whose leaves all fail to satisfy ψ. Moreover, since γ C 1 ; C 3 , s, h by the operational semantics, there is a maximal subtree κ of κ with root C 1 ; C 3 , s, h and whose leaves also all fail to satisfy ψ. Clearly height(κ ) < height(κ).
Finally, if there is a precondition trace (Ψ (E), Ψ (E)) following the edge then Ψ (E) = Ψ (E ) by Definition 11. Since (s, h) | P and Ψ (E) is a predicate formula in P, we have (s, h 0 ) | Ψ (E) for some subheap h 0 of h. Therefore a smallest approximant α such that Assume the conclusion of the rule is invalid. Pick an arbitrary program state such that (s, h) | P but γ = if * then C 1 else C 2 fi ; C 3 , s, h | ϕ. By Definition 7, if γ | ϕ then there exists γ such that γ γ and γ | ϕ. By the operational semantics we know that there are two possibilities: γ = C 1 ; C 3 , s, h or γ = C 2 ; C 3 , s, h . Hence, either C 1 ; C 3 , s, h | ϕ or C 2 ; C 3 , s, h | ϕ. We show the first of these cases, the second being similar. Case γ = C 1 ; C 3 , s, h .

By our assumption (s, h) | P and invalidity result γ | ϕ then it is the case that the left-most premise is invalid.
If there is a -trace following the edge, then by Definition 10, ϕ = AGψ. Furthermore, since γ | AGψ, then by Definition 7 we know that there existsγ such that γ * γ and γ | ψ (call this path π ). Finally, since by the operational semantics γ γ , then π is a subpath of π from γ to γ ¬, and moreover length(π ) < length(π).
Given the structure of the temporal formula ϕ/ ϕ, there cannot be a ♦-trace following either of the edges.
The situation for precondition traces is similar to the previous case.

Case (Unfold-Pre)
Assume the conclusion of the rule is invalid. Pick an arbitrary program state such that (s, h) | Π : Ψ (E) * Σ but C, s, h | ϕ. By Definition 2 we can split h into two disjoint subheaps h = h h so that (s, h ) | Π : Ψ (E) and (s, h ) | Π : Σ. Since (s, h ) | Π : Ψ (E) then by Definition 2 we know that ( E s, h ) ∈ Ψ . Moreover, by Definition 5 we know that the program state (s, h ) is in the αth approximant of Ψ for some smallest α (i.e. ( E s, h ) ∈ Ψ α ). By construction of the definition set operator for Ψ (Definition 4), it follows that there is some inductive rule Π j : Σ j ⇒ Ψ (E)) such that (s, h ) | Π j : Σ j (we ignore variable renaming issues for simplicity). We choose J i+1 to be the jth premise of the rule, Π ∪ Π j : Σ * Σ j C : ϕ, and our falsifying state to be (s, h). It follows that (s, h) | Π ∪ Π j : Σ * Σ j , and so the premise J i+1 is not valid, as required.
If there is a -trace following the edge (J i , J i+1 ), then by Definition 10, ϕ = AGψ or ϕ = AGψ. Furthermore, since by our previous invalidity result C, s, h | ϕ, then by Definition 7 we know that there existsγ such that C, s, h * γ andγ | ψ (call this path π). Thus the paths π = π in item 1 of the lemma are well defined and, trivially, length(π ) ≤ length(π).
If there is a ♦-trace following the edge (J i , J i+1 ), then by Definition 10, ϕ = EGψ or ϕ = ♦EGψ. Furthermore, since by our previous invalidity result C, s, h | ϕ, then by Definition 7 we know that for all paths π starting from C, s, h there exists a configuration γ ∈ π such thatγ | ψ (call this tree κ. Thus the trees κ = κ in item 2 of the lemma are well-defined and, trivially, height(κ ) ≤ height(κ).
If there is a precondition trace (Ψ (E), Ψ (E )) following the edge then Ψ (E ) is an unfolding of Ψ (E) appearing in Σ j . Since ( E s, h ) ∈ Ψ α , it follows that ( E s, h 0 ) ∈ Ψ β for some subheap h 0 of h and some approximant β < α. This completes the case.

Theorem 1 (Soundness) If P C : ϕ is provable, then it is valid.
Proof Suppose for contradiction that there is a cyclic proof P of J = P C : ϕ but J is invalid. That is, for some stack s and heap h, we have (s, h) | P but C, s, h | ϕ. Then, by local soundness of the proof rules (Lemma 1), we can construct an infinite path (P i C i : ϕ i ) i≥0 in P of invalid judgements. Since P is a cyclic proof, by Definition 12 there exists an infinitely progressing trace following some tail (P i C i : ϕ i ) i≥n of this path.
If this trace is a -trace, using condition 1 of Lemma 1, we can construct an infinite sequence of finite paths to a fixed configuration γ of infinitely decreasing length, contradiction. If the trace is a ♦-trace, we can construct an infinite sequence of execution trees (whose leaves are again fixed configurations) of infinitely decreasing height, contradiction. Finally, a precondition trace yields an infinitely decreasing sequence of ordinal approximations of some inductive predicate, also a contradiction. The conclusion is that P C : ϕ must be valid after all.
We remark that the dichotomy between inductive and coinductive arguments can be discerned in our trace condition. Coinductive (G) properties need to show that something happens infinitely often whereas inductive (F) properties have to show that something cannot happen infinitely often. Both cases give us a progress condition: for coinductive properties, we essentially need infinite program progress on the right of judgements, whereas for inductive properties we need an infinite descent on the left of judgements (or the proof to be finite).
Readers familiar with Hoare-style proof systems might wonder about relative completeness of our system, i.e., whether all valid judgements are derivable if all valid entailments between formulas are derivable. Typically, such a result might be established by showing that for any program C and temporal property ϕ, we can (a) express the logically weakest precondition for C to satisfy ϕ, say wp(C, ϕ), and (b) derive wp(C, ϕ) C : ϕ in our system. Relative completeness then follows from the rule of consequence, (Cons). Unfortunately, it seems certain that such weakest preconditions are not expressible in our language. For example, in [7], the multiplicative implication of separation logic, - * , is needed to express weakest preconditions, whereas it is not present in our language due to the problems it poses for automation (a compromise typical of most separation logic analyses). Indeed, it seems likely that we would need to extend our precondition language well beyond this, since [7] only treats termination, whereas we treat arbitrary temporal properties. Since our focus in this paper is on automation, we leave such an analysis to future work.

Fairness
An important component in the verification of reactive systems is a set of fairness requirements to guarantee that no computation is neglected forever. In this section, we show how our cyclic proof system for CTL program properties can be adapted to incorporate such fairness constraints when verifying nondeterministic programs.
These fairness constraints are usually categorised as weak and strong fairness [20]. However, since weak fairness requirements are usually restricted to the parallel composition of processes, a property that our programming language lacks, we limit ourselves to the treatment of strong fairness.

Definition 13 (Fair execution)
Let C be a program command and π = (π i ) i≥0 a program execution. We say that π visits C infinitely often if there are infinitely many distinct i ≥ 0 such that π i = C, _ , _ . A program execution π is fair for commands C i , C j if it is the case that π visits C i infinitely often if and only if π visits C j infinitely often. Furthermore, π is fair for a program C if it is fair for all pairs of commands C i , C j such that C contains a command of the form if * then C i else C j fi or while * do C i od ; C j .
Note that every finite program execution is trivially fair. Also, for the purposes of fairness, we consider program commands to be uniquely labelled (to avoid confusion between different instances of the same command).

Remark 2
One can argue that Definition 13 does not consider programs of the form while * do C i od. Whereas this program, being finite, would be considered fair, one can also imagine more involved scenarios with nested branching commands which difficult the establishment of a clear continuation for a while command.
In these cases, we can see programs of the form while * do C i od as having a no-op continuation resulting in programs of the form while * do C i od ; skip to avoid confusion.
We now modify our cyclic CTL system to treat fairness constraints. First, we adjust the interpretation of judgements to account for fairness, then we lift the definition of fairness from program executions to paths in a pre-proof. Definition 14 (Fair CTL judgement) A fair CTL judgement P f C : ϕ is valid if and only if, for all memory states (s, h) such that s, h | P, we have C, s, h | f ϕ, where | f is the satisfaction relation obtained from | in Definition 7 by interpreting the temporal operators as quantifying over fair paths, rather than all paths. For example, the clause for AG becomes γ | f AGϕ ⇔ ∀ f airπ starting from γ. ∀γ ∈ π. γ | f ϕ.

Definition 15
A path in a pre-proof (J i = P i f C i : ϕ i ) i≥0 is said to visit C infinitely often if there are infinitely many distinct i ≥ 1 such that J i C = C and the rule applied at J i−1 is a symbolic execution rule. A path in a pre-proof is fair for commands C i , C j if it is the case that (J i ) i≥0 visits C i infinitely often if and only if it visits C j infinitely often. Finally, the path is fair for program C iff it is fair for all pairs of commands C i , C j such that C contains a command of the form if * then C i else C j fi or while * do C i od ; C j .
Intuitively, to account for fairness constraints, we simply need to restrict the global soundness condition from Definition 12 so that it quantifies over all fair infinite paths in a pre-proof, ignoring unfair paths. However, as it stands, this intuition is not quite correct. Under precondition x = 1, this program has the CTL property EG(x = 1) owing precisely to the unfair execution that always favours the first branch of the nondeterministic if. We can witness this using a cyclic proof with a single loop that invokes the rule (If*♦1) infinitely often. The infinite path created by this loop is unfair and thus such a proof should not count as a "fair" cyclic proof. However, if we simply ignore this infinite path, the only one in the pre-proof, then the global soundness condition is trivially satisfied! Our answer is to take a more subtle view of the roles played by existential (EG/E F/♦) and universal (AG/AF/ ) properties; unfair paths created by the former must be disallowed, whereas unfair paths created by the latter can simply be disregarded. i≥0 in P such that, given a program point C, the rule (Wh*♦1) or (If*♦1) is applied to infinitely many distinct J i such that J i C = C and (Wh*♦2) or (If*♦2) is applied to only finitely many distinct J i such that J i C = C or vice versa.

Definition 17 (Fair cyclic proof)
A pre-proof P is a fair cyclic proof if it is not bad in the sense of Definition 16 above and, for every infinite fair path (P i f C i : ϕ i ) i≥0 in P, there is an infinitely progressing -trace, ♦-trace or precondition trace following some tail (P i f C i : ϕ i ) i≥n of the path.

Proposition 2 (Decidable soundness condition)
It is decidable whether a pre-proof is a valid fair cyclic proof. Fig. 6 Fair CTL cyclic proof example. We write C i to indicate atomic command C labelled with program counter i Proof (Sketch) Given a pre-proof P, we first have to check that it is not bad in the sense of Definition 16. This can be done using Büchi automata: given a particular program point C, one can construct automata B 1 and B 2 accepting the infinite paths in P such that (Wh*♦1) or (If*♦1), respectively (Wh*♦2) or (If*♦2) are applied infinitely often to C. We simply check , which is decidable (repeating this exercise for each needed program point).
It just remains to check that there exists an infinitely progressing trace along every infinite fair path of P. The argument is similar to the argument for our non-fair cyclic proofs, except that instead of an automaton accepting all infinite paths of P we now require one accepting only fair infinite paths. This can be done using a Streett automaton with an acceptance condition of conjuncts of the form (Fin(i) ∨ Inf( j)) ∧ (Fin( j) ∨ Inf(i)) for each pair of fairness constraints (i, j). We are then done since Streett automata can be transformed into Büchi automata [21].
To illustrate the concepts introduced in this section, we show a fair cyclic CTL proof of the following example.

Example 5 Consider the following labelled program C:
1 : while ( x = x ) do 2 : while (*) do 3 : x := true ; od ; 4 : x := false ; od ; While the property AF(x = false) fails for this program in general, it becomes true under the fairness constraint (C 3 , C 4 ). Figure 6 shows an abridged proof of this property in our fair cyclic CTL proof system, where the application of a ∨ rule following each (AF) rule has been omitted from the proof for brevity. Note the formation of a cycle on the leftmost branch, along which there is no trace. Whereas this cycle invalidates the general CTL soundness condition, it does not invalidate the fair CTL soundness condition, since the path in question visits C 3 infinitely often but not C 4 ; hence it is unfair as per Definition 15 (but not bad in the sense of Definition 16). Hence, the fair global soundness condition is trivially satisfied as there are only finite fair paths in the pre-proof. Consequently, the pre-proof qualifies as a fair CTL cyclic proof.

Example 6
We return to our server program from Examples 1 and 4. Suppose we wish to prove, not that it is always possible for the heap to become empty, i.e. AG E F(emp), but  Figure 7 shows an abridged proof of this property in the our fair cyclic CTL proof system. Adding the fairness constraints relaxes the conditions under which back-links can be formed. The back-links depicted in green induce infinite paths with no valid trace. However, because these infinite paths are unfair (and not bad), they are not considered in the global soundness condition. Our pre-proof thus qualifies as a fair cyclic proof since along every fair infinite path there is either a -trace or a precondition trace progressing infinitely often.
Proof (Sketch) Suppose for contradiction that there is a fair cyclic proof P of J = P f C : ϕ but J is invalid. That is, for some stack s and heap h, we have (s, h) | P but C, s, h | f ϕ. Then, by local soundness of the proof rules, we can construct an infinite path Π = (P i f C i : ϕ i ) i≥0 in P of invalid sequents. We have an infinitely progressing trace along this path and can thus obtain a contradiction exactly as in the proof of soundness for the standard system (Theorem 1) provided that Π is fair.
Suppose therefore that Π is unfair for commands (C i , C j ) say. We consider the case in which C contains the command if * then C i else C j fi and Π visits C i infinitely often and C j only finitely often; the while case is similar. Using Definition 15 we know that if * then C i else C j fi itself is symbolically executed infinitely often on Π. It cannot be that (If * ♦1) is applied infinitely often and (If * ♦2) only finitely often on Π, since Π would in that case be a bad path, which is specifically excluded by Definition 16. Nor can it be that both rules are applied infinitely often, since in that case Π would be fair for (C i , C j ), contrary to assumption.
The only remaining possibility is that (If * ) is applied infinitely often on Π. In that case it must be that Π contains infinitely many occurrences of the left premise of the rule and only finitely many instances of the right premise (or vice versa). Hence the program execution underlying the pre-proof path Π is also unfair. Since the satisfaction relation | f is restricted to fair program executions, this contradicts the assumption that C, s, h | f ϕ. (The full justification of this last step requires the observation that, in order to produce a infinitely often, the underlying temporal property must contain an outermost AF or AG quantifier.)

Implementation and Evaluation
We implement our proof systems on top of the Cyclist [9], a mechanised cyclic theorem proving framework. The implementation, source code and benchmarks are publicly available at [1].
Our implementation performs iterative depth-first search, aimed at closing open nodes in the proof by either applying an inference rule or forming a back-link. If an open node cannot be closed, we attempt to apply symbolic execution; if this is not possible, we try unfolding temporal operators and inductive predicates in the precondition to enable symbolic execution to proceed. Forming back-links typically requires the use of the consequence rule (i.e. a lemma proven on demand) to re-establish preconditions altered by symbolic executions (as can be seen in Figs. 5 and 7). When forming a back-link, we check automatically that the global soundness condition is satisfied. Entailment queries over symbolic heaps in separation logic, which arise at backlinks and when applying the (Check) axiom or checking rule side conditions, are handled by a separate instantiation of Cyclist for separation logic entailments [9].
We evaluate the implementation on handcrafted nondeterministic and nonterminating programs similar to Example 1. Our test suite is essentially an adaptation of the model checking benchmarks in [14,15] for temporal properties of nondeterministic programs. Roughly speaking, we replace operations on integer variables by analogous operations on heap data structures.
Our test suite comprises the following programs: • Examples discussed in the paper are named Exmp; • Fin-Lock -a finite program that acquires a lock and, once obtained, proceeds to free from memory the elements of a list and reset the lock; • Inf-Lock wraps the previous program inside an infinite loop; • Nd-In-Lock is an infinite loop that nondeterministically acquires a lock, then proceeds to perform a nondeterministic number of operations before releasing the lock; • Inf-List is an infinite loop that nondeterministically adds a new element to the list or advances the head of the list by one element on each iteration; • Insert-List has a nondeterministic if statement that either adds a single elements to the head of the list or deletes all elements but one, and is followed by an infinite loop; • Append-List appends the second argument to the end of the first argument; • Cyclic-List is a nonterminating program that iterates through a non-empty cyclic list; • Inf-BinTree is an infinite loop that nondeterministically inserts nodes to a binary three or performs a random walk of the three; • The programs named with Branch define a somewhat arbitrary nesting of nondeterministic if and while statements, aimed at testing the capability of the tool in terms of lines of code and nesting of cycles; • Finally we also cover sample programs taken from the Windows Update system (Win Update), the back-end infrastructure of the PostgreSQL database server (PostgreSQL) and an implementation of the acquire-release algorithm taken from the aforementioned benchmarks (Acq-Rel).
We show the results of the evaluation of the CTL system and its fair extension in Table 1. For each test, we report whether fairness constraints were needed to verify the desired property and the time taken in seconds (we use TO in case the tool failed to produce a proof within 60 seconds). The tests were carried out on an Intel x-64 i5 system at 2.50 GHz.
Our experiments demonstrate the viability of our approach: our runtimes are mostly in fractions of seconds and show similar performance to existing tools for the model checking benchmarks. Overall, the execution times in the evaluation are quite varied, as they depend on factors such as the complexity of the program and temporal property in question, but sources of potential slowdown can be witnessed by different test cases. Even at the level of pure memory assertions, the base case rule (Check) has to check entailments P | Q between symbolic heaps, which involves calling an inductive theorem prover; this is reasonably fast in some cases, but very costly in others (e.g. the Append-List example). Another source of slowdown is in attempting to form back-links too eagerly (e.g. when encountering the same command at two different program locations); since we check soundness when forming a back-link, which involves calling a model checker (cf. [9]), this too is an expensive operation, as can be seen in the runtimes of test cases with suffix Branch.
Notwithstanding the broadly encouraging results, our implementation is not without limitations; in some cases, the proof search will fail to terminate. Some of this cases are due to the invalidity of the jugment that is intended to be demonstrated (as in is the case of Exmp, Fin-Lock and Nd-In-Lock) whereas in some other cases we fail to establish a sufficiently general "invariant" to form backlinks in the proof (the case of Inf-BinTree).

Related Work
Related work on the automated verification of temporal program properties can broadly be classified into two main schools, model checking and deductive verification. In recent years, model checking has been the more popular of these two. Although earlier work in model checking focused on finite-state transition systems (e.g. [11,25]), recent advances in areas such as state space restriction [3], precondition synthesis [13], CEGAR [15], bounded model checking [10] and automata theory [12] have enabled the treatment of infinite transition systems.
The present paper takes the deductive verification approach. In the realm of infinite state, previous proof systems for verifying temporal properties of arbitrary transition systems [23,29] have shed some light on the soundness and relative completeness of deductive verification. Of particular relevance here are those proof systems for temporal properties based on cyclic proof.
Our work can be seen as an extension to arbitrary temporal properties of the cyclic termination proofs in Brotherston et al. [7]. A procedure for the verification of CTL* properties that employs a cyclic proof system for LTL as a sub-procedure was developed by Bath et al. [4]. A subtle but important difference when compared with our work is the lack of cut/consequence rule (used e.g. to generalise precondition formulas or to apply intermediary lemmas). A side benefit of this restriction is a simplification of the soundness condition on cyclic proofs. A cyclic proof system for the verification of CTL* properties of infinite-state transition systems is presented in Sprenger's PhD thesis [29]. Focusing on generality, this system avoids considering details of state formulas and their evolution throughout program execution by assuming an oracle for a general transition system. The system relies on a soundness condition that is similar to Definition 12, but does not track progress in the same way, imposing extra conditions on the order in which rules are applied. The success criterion for validity of a proof also presents some differences; it relies on finding ranking functions, intermediate assertions and checking for the validity of Hoare triples, and it is far from clear that such checks can be fully automated. In contrast, we rely on a relatively simple ω-regular condition, which is decidable and can be automatically checked by Cyclist [5,9,30].

Conclusions and Future Work
Our main contribution in this paper is the formulation, implementation and evaluation of a deductive cyclic proof system for verifying temporal properties of pointer programs, building on previous systems for separation logic and for other temporal verification settings [4,7,29]. We present two variants of our system and prove both systems sound. We have implemented these proof systems, and proof search algorithms for them, in the Cyclist theorem prover, and evaluated them on benchmarks drawn from the literature.
The main advantage of our approach is that we never obtain false positive results. This advantage is not in fact exclusive to deductive verification: some automata-theoretic model checking approaches are also proven to be sound [33]. Nonetheless, when compared to such approaches, our treatment of the temporal verification problem has the advantage of being direct. Owing to our use of separation logic and a deductive proof system, we do not need to apply approximation or transformations to the program as a first step; in particular, we avoid the translation of temporal formulas into complex automata [34] and the instrumentation of the original program with auxiliary constructs [12].
One natural direction for future work is to develop improved mechanised techniques, such as generalisation / abstraction, to enhance the performance of proof search in our system(s).
Another possible direction is to consider larger classes of programs. In particular, concurrency is one very interesting such possibility, perhaps building on existing verification techniques for concurrency in separation logic (e.g. [32]). A different direction to explore is the enrichment of our assertion language, for example to CTL* [17] or μ-calculus [16]. The structure of CTL* formulas and their classification into path and state subformulas suggest a possible combination of our CTL system with an LTL system to produce a proof object composed of smaller proof structures (cf. [4,29]). The encoding of CTL* into μ-calculus [16] and the applicability of cyclic proofs for the verification of μ-calculus properties (see e.g. [28]) hint at the feasibility of such an extension.
Open Access This article is distributed under the terms of the Creative Commons Attribution 4.0 International License (http://creativecommons.org/licenses/by/4.0/), which permits unrestricted use, distribution, and reproduction in any medium, provided you give appropriate credit to the original author(s) and the source, provide a link to the Creative Commons license, and indicate if changes were made.