1 Introduction
1.1 Safety proofs of parameterized programs
s
and t
and parameter N whose source code and control-flow graph are shown in Fig. 1a, b, respectively.1 Assume that T is executed by an arbitrary number of n threads, where each thread runs the program \(P = T[N/n]\) obtained by replacing parameter N by the number of threads \(n > 0\) in program template T (Fig. 1c). We write \(P(n) = P_1 \parallel \dots \parallel P_n\) for this parameterized program (Fig. 1d). Figure 1e shows the instance P(3) of parameterized program P(n) for size \(n=3\). In this paper, we show how to automatically prove that the error location \(\ell _\textit{err}\) is unreachable for all threads from an initial state of, e.g., \(s=t=0\) for all system sizes \(n>0\). That is, we prove safety of the infinite family \(\{P(1), P(2), \dots \}\) of instances induced by the parameterized program P(n).
1.1.1 Ingredients of a safety proof
s
and t
in a meaningful way. For example, note that the value of global variable t
equals the number of threads at either control location \(\ell _1\), \(\ell _2\), or \(\ell _\textit{err}\). Similarly, the value of s
equals the number of threads at control location \(\ell _2\). Indeed, the safety proof of the assertion’s lower bound \(0 < t-s\) requires finding the invariant1.1.2 Role of our abstraction
1.2 Termination proofs of parameterized programs
x
and then variable d
are assigned positive integer values (\(\ell _0\rightarrow \ell _1\) and \(\ell _1\rightarrow \ell _2\), respectively). The subsequent loop subtracts the value of d
from x
(\(\ell _3\rightarrow \ell _2\)) until x
becomes non-positive (\(\ell _2\rightarrow \ell _3\)).
1.2.1 Ingredients of a termination argument
d
is always positive, the value of x
strictly decreases in the loop \(\ell _2 \rightarrow \ell _3 \rightarrow \ell _2 \rightarrow \dots\). Eventually, the value of x
becomes non-positive. The loop’s execution is guarded by the assume statement \([\texttt {x} > 0]\), i.e., once the value of x
falls below one, the program stops executing and terminates.x
an unbounded number of times. The value of x
may never become non-positive, causing the reference thread’s loop to diverge.x
happens at most a fixed number (viz. \(n-1\)) of times while some thread is within the loop. Still, to prove termination of the infinite family of fixed-size instances \(\{\mathcal {P}(1), \mathcal {P}(2), \dots \}\) induced by the parameterized program \(\mathcal {P}(n)\), a naive approach would need to repeat this argument for each of the infinite number of system sizes \(n>0\). In addition, such a proof needs to keep track of the progress of unboundedly-many threads, and thus may need to refer to their unboundedly-many local states.1.2.2 Role of our abstraction
-
can be verified in a single run of an off-the-shelf termination prover for sequential software, and
-
implies termination of all infinitely-many instances of the parameterized program at once.
1.3 Parameterized programs: two dimensions of infinity
2 Related work
2.1 Existing methods for safety verification
2.2 Existing methods for termination proving
2.3 Our approach
3 Contributions
4 Motivating example: safety
4.1 Overall idea: thread-modular abstraction
4.1.1 Counter instrumentation
4.1.2 Thread-modular summary generation
4.2 Invariant generation (predicate abstraction + CEGAR)
5 Program model and parameterized safety
*
denotes a non-deterministic integer value. We write \(\nu ({\textbf {g}} , {\textbf {l}} )\) for the valuation of global and local variables and omit its arguments wherever clear from the context. Further, we denote by \({\nu }{\downharpoonright _{{\textbf {v}}}}\) the projection of valuation \(\nu\) to variables \({\textbf {v}}\) and extend its definition to sets and relations in the usual, element-wise way. We denote by \(\nu ' \in \llbracket \textit{gc}\rrbracket (\nu )\) the (possibly non-deterministic) effect of a guarded command \(\textit{gc}\) and write \(\varphi ({\textbf {g}} ,{\textbf {g}} ',{\textbf {l}} ,{\textbf {l}} ')\) for its standard encoding as a formula over primed and unprimed variables.6 Tackling infinity dimension I: thread-modular counter abstraction
6.1 Control counter abstraction (CCA)
6.2 Thread-modular summary generation (TMS)
6.3 Combining CCA and TMS: thread-modular counter abstraction (TMCA)
6.4 Thread-modular counter abstraction (TMCA) vs. standard thread-modular reasoning (TM)
6.5 Conclusion
7 Tackling infinity dimension II: predicate abstraction (PA)
7.1 Predicate selection for TMCA models
c
\(_0\), c
\(_1\), c
\(_2\), s
, and t
. Tracking the value of either one leads to useless loop unrollings.c
\(_0\), c
\(_1\), c
\(_2\) to the values of the global variables s
and t
. Thus, traditional predicate selection heuristics do not apply, and we need a new selection procedure suitable for TMCA models (and similarly structured programs). The next section explains how to achieve this.7.2 An interpolation abstraction heuristic for TMCA models
s
and t
correspond to the number of threads in specific control locations, and thus to sums over the instrumented location counters. In particular, at \(\ell _1\) we have that8 Safety experiments
8.1 Benchmarks
fkp2014
and the bluetooth driver qw2004
are the introductory and running example of [25].8.2 Comparisons
-abstract:relIneqs
, column 1b), Eldarica without interpolation abstraction (-abstract:off
, column 1c) and the CHC solver in Z3 [20] (column 1d).8.3 Results
parent–child
and as-many
, use dynamic thread creation which is currently not supported by Eldarica. Eldarica times out on the remaining ones. Unfortunately, we were unable to replicate the results for Pacman from [30]. We are thus limited to citing previous results from [30] (recall from Sect. 2 that our main objective is to replace their dedicated abstraction techniques with a cleaner framework of well-established ones).maximum
does not have a thread-modular proof and thus cannot be proved safe by our method. On the remaining benchmarks, TMCA (Sect. 6) combined with our predicate selection heuristic (Sect. 7) is the only toolchain to solve all tasks. It does so well below the timeout limit of 15 min. Meanwhile, Eldarica with default predicate selection heuristics encounters 5 timeouts, Eldarica without interpolation abstraction 10, and Z3 in its standard configuration even 11 timeouts. This shows how indispensable an appropriate predicate discovery algorithm is for our thread-modular abstractions.9 Case study: generalization to termination
9.1 Introduction
x
and then d
are assigned positive integer values. The subsequent loop subtracts d
from x
until x
becomes non-positive. We are interested in showing termination of the parameterized program \(\mathcal {P}(n) = \mathcal {P}_1 \parallel \dots \parallel \mathcal {P}_n\) composed of n threads executing \(\mathcal {P}\) for all \(n>0\).d
is positive \([ \texttt {d} > 0]\) in transition \(\textsf{Sub}\). This is in accordance with our definition of thread-modular summaries (Definition 4). Methods for computing such summaries have been described throughout the literature, for example in [69, 70].d
is strictly positive once the concrete thread \(\mathcal {P}_1\) reaches \(\ell _2\). The remaining environment interference that could cause the program to diverge is transition \(\textsf{AssX}\) that resets the value of \(\texttt {x}\). At this point, TMCA’s counter instrumentation comes into play. While \(\textsf{AssX}\) resets the value of x
and may thus prevent its value from converging towards 0, the number of times transition \(\textsf{AssX}\) can be executed is unbounded but finite: due to its guard \([\texttt {c}_1 > 0]\), this transition may not execute more than \(n-1\) times. This symbolic expression is itself unbounded from above, but only takes finite, non-negative values. After at most \(n-1\) executions of \(\textsf{AssX}\), the value of \(\texttt {x}\) cannot be reset anymore, and progresses towards zero in the loop \(\ell _2 \rightarrow \ell _3 \rightarrow \ell _2 \rightarrow \dots\) as above (and potentially also by interleaved executions of \(\textsf{PosX}\) and \(\textsf{Sub}\) by the abstracted environment \({\hat{\mathcal {P}}}\)).x
progress towards zero, and thus cannot be executed indefinitely.