Abstract
While automated verification of imperative programs has been studied intensively, proving termination of programs with explicit pointer arithmetic fully automatically was still an open problem. To close this gap, we introduce a novel abstract domain that can track allocated memory in detail. We use it to automatically construct a symbolic execution graph that over-approximates all possible runs of a program and that can be used to prove memory safety. This graph is then transformed into an integer transition system, whose termination can be proved by standard techniques. We implemented this approach in the automated termination prover AProVE and demonstrate its capability of analyzing C programs with pointer arithmetic that existing tools cannot handle.
Notes
This LLVM program corresponds to the code obtained from strlen with the Clang compiler [23]. To ease readability, we wrote variables without “%” in front (i.e., we wrote “str” instead of “%str” as in proper LLVM) and added line numbers.
We use “\(\hookrightarrow \)” instead of “\(\mapsto \)” in separation logic, since \( mem \models n_1 \mapsto n_2\) would imply that \( mem (n)\) is undefined for all \(n \ne n_1\). This would be inconvenient in our formalization, since \( PT \) usually only contains information about a part of the allocated memory.
A corresponding representation could also be defined for big-endian layout. This layout information is necessary to decide which concrete states are represented by abstract states, but it is not used when constructing symbolic execution graphs (i.e., our remaining approach is independent of such layout information).
We identify sets of first-order formulas \(\{\varphi _1,\ldots , \varphi _n\}\) with their conjunction \(\varphi _1 \wedge \cdots \wedge \varphi _n\). Thus, \( CS \) is identified with the set resp. with the conjunction of the equations \(\bigcup _{1 \le i \le n} \{ \texttt {x}_i = LV _i(\texttt {x}) \mid \texttt {x} \in \mathcal {V}_{\mathcal {P}}, LV _i(\texttt {x}) \text { is defined}\}\). Moreover, we wrote to ensure that this part of the formula is \( true \) if \( AL ^*= \varnothing \).
The reason is that then there is an address \( end \in {\mathbb {N}}_{>0}\) with \( end \ge as ^c(\texttt {str}_1)\) such that \( mem ^c( end ) = 0\) and \( mem ^c\) is defined for all numbers between \( as ^c(\texttt {str}_1)\) and \( end \). Hence if a is the state in (\(\dagger \)), then \( mem ^c \models \sigma (\langle {a}\rangle _{ SL })\) holds for any instantiation \(\sigma \) with \(\sigma (u_{\texttt {str}}) = as ^c(\texttt {str}_1)\), \(\sigma (v_{ end }) = end \), and \(\sigma (z) = 0\).
For any terms, “\(\llbracket {}t_1,\,t_2\rrbracket \; \bot \; \llbracket {}\overline{t_1},\,\overline{t_2}\rrbracket \)” is a shorthand for \(t_2< \overline{t_1} \vee \overline{t_2} < t_1\).
Analogous refinement rules can also be used for other conditional LLVM instructions, e.g., conditional jumps with br or other cases of icmp.
Since we do not consider struct data structures in this paper, we disregard getelementptr instructions with more than two parameters. Note that getelementptr instructions with just two parameters suffice for several levels of de-referencing (where memory has to be accessed after each getelementptr instruction).
Evaluation edges are edges that are not refinement or generalization edges.
For programs starting in states represented by an abstract state \(a_0\), it would suffice to prove termination of all \(\rightarrow _{{\mathcal {I}}}\)-evaluations starting in ITS states of the form \((a_0,\sigma )\).
In the transition, we do not impose the additional constraints of \(\langle {\overline{a}}\rangle \) on the post-variables \({\mathcal {V}}'\), since they are checked anyway in the next transition which starts in \(\overline{a}\).
The instructions supported by our implementation are icmp (eq,ne,sgt,sge,slt,sle, ugt,uge,ult,ule), add, sub, mul, sdiv, srem, urem, and, or, xor, shl, ashr, lshr, call, br, bitcast, ptrtoint, trunc, sext, zext, getelementptr (with at most 2 parameters), select, phi, ret, alloca, load, and store.
As mentioned above, we also started implementing support for non-termination in AProVE. When running the tools on all 631 C examples, AProVE proves termination for 409 and non-termination for 91 examples. Ultimate shows termination for 392 and non-termination for 111 programs. Finally, HipTNT+ proves termination in 312 and non-termination in 107 cases. Again, the detailed results can be found at [3].
References
Albarghouthi, A., Li, Y., Gurfinkel, A., Chechik, M.: Ufo: A framework for abstraction- and interpolation-based software verification. In: Proceedings of CAV’12
Albert, E., Arenas, P., Codish, M., Genaim, S., Puebla, G., Zanardini, D.: Termination analysis of Java Bytecode. In: Proceedings of FMOODS’08
AProVE. http://aprove.informatik.rwth-aachen.de/eval/PointerJournal/
Berdine, J., Cook, B., Distefano, D., O’Hearn, P.W.: Automatic termination proofs for programs with shape-shifting heaps. In: Proceedings of CAV’06
Berdine, J., Chawdhary, A., Cook, B., Distefano, D., O’Hearn, P.W.: Variance analyses from invariance analyses. In: Proceedings of POPL’07
Berdine, J., Cook, B., Ishtiaq, S.: SLAyer: Memory safety for systems-level code. In: Proceedings of CAV’11
Bertot, Y., Castéran, P.: Coq Art. Springer, 2004
Blanqui, F., Koprowski, A.: CoLoR: A Coq library on well-founded rewrite relations and its application to the automated verification of termination certificates. Math. Struct. Comput. Sci. 4, 827–859 (2011)
Bodin, M., Jensen, T., Schmitt, A.: Certified abstract interpretation with pretty-big-step semantics. In: Proceedings of CPP’15
Bouajjani, A., Bozga, M., Habermehl, P., Iosif, R., Moro, P., Vojnar, T.: Programs with lists are counter automata. Formal Methods Syst. Design 38(2), 158–192 (2011)
Brockschmidt, M., Otto, C., von Essen, C., Giesl, J.: Termination graphs for Java Bytecode. In: Verification, Induction, Termination Analysis, LNAI 6463, (2010)
Brockschmidt, M., Otto, C., Giesl, J.: Modular termination proofs of recursive Java Bytecode programs by term rewriting. In: Proceedings of RTA’11
Brockschmidt, M., Ströder, T., Otto, C., Giesl, J.: Automated detection of non-termination and NullPointerExceptions for Java Bytecode. In: Proceedings of FoVeOOS’11
Brockschmidt, M., Musiol, R., Otto, C., Giesl, J.: Automated termination proofs for Java programs with cyclic data. In: Proceedings of CAV’12
Brockschmidt, M., Cook, B., Fuhs, C.: Better termination proving through cooperation. In: Proceedings of CAV’13
Brotherston, J., Gorogiannis, N.: Cyclic abduction of inductively defined safety and termination preconditions. In: Proceedings of SAS’14
Cachera, D., Pichardie, D.: A certified denotational abstract interpreter. In: Proceedings of ITP’10
Cadar, C., Dunbar, D., Engler, D.R.: KLEE: Unassisted and automatic generation of high-coverage tests for complex systems programs. In: Proceedings of OSDI’08
Calcagno, C., Distefano, D., O’Hearn, P.W., Yang, H.: Beyond reachability: Shape abstraction in the presence of pointer arithmetic. In: Proceedings of SAS’06
Calcagno, C., Distefano, D., O’Hearn, P.W., Yang, H.: Space invading systems code. In: Proceedings of LOPSTR’08
Calcagno, C., Distefano, D.: Infer: An automatic program verifier for memory safety of C programs. In: Proceedings of NFM’11
Chen, H.Y., David, C., Kroening, D., Schrammel, P., Wächter, N.: Synthesising interprocedural bit-precise termination proofs. In: Proceedings of ASE’15
Clang compiler. http://clang.llvm.org
Clarke, E.M., Grumberg, O., Jha, S., Lu, Y., Veith, H.: Counterexample-guided abstraction refinement for symbolic model checking. J. ACM 50(5), 752–794 (2003)
Contejean, E., Courtieu, P., Forest, J., Pons, O., Urbain, X.: Automated certified proofs with CiME3. In: Proceedings of RTA’11
Cook, B., Podelski, A., Rybalchenko, A.: Abstraction refinement for termination. In: Proceedings of SAS’05
Cook, B., Podelski, A., Rybalchenko, A.: Termination proofs for systems code. In: Proceedings of PLDI’06
Cook, B., Podelski, A., Rybalchenko, A.: Summarization for termination: no return!. Formal Methods Syst. Design 35(3), 369–387 (2009)
Cousot, P., Halbwachs, N.: Automatic discovery of linear restraints among variables of a program. In: Proceedings of POPL’78
David, C., Kroening, D., Lewis, M.: Unrestricted termination and non-termination arguments for bit-vector programs. In: Proceedings of ESOP’15
de Moura, L., Bjørner, N.: Z3: An efficient SMT solver. In: Proceedings of TACAS’08
D’Silva, V., Urban, C.: Conflict-driven conditional termination. In: Proceedings of CAV’15
Dudka, K., Peringer, P., Vojnar, T.: Predator: A shape analyzer based on symbolic memory graphs (competition contribution). In: Proceedings of TACAS’14
Dutertre, B., de Moura, L.: The Yices SMT solver. Tool paper at http://yices.csl.sri.com/tool-paper.pdf
Falke, S., Kapur, D., Sinz. C.: Termination analysis of C programs using compiler intermediate languages. In: Proceedings of RTA’11
Falke, S., Merz, F., Sinz, C.: LLBMC: Improved bounded model checking of C using LLVM (competition contribution). In: Proceedings of TACAS’13
Fuhs, C., Giesl, J., Plücker, M., Schneider-Kamp, P., Falke, S.: Proving termination of integer term rewriting. In: Proceedings of RTA’09
Giesl, J., Brockschmidt, M., Emmes, F., Frohn, F., Fuhs, C., Otto, C., Plücker, M., Schneider-Kamp, P., Ströder, T., Swiderski, S., Thiemann, R.: Proving termination of programs automatically with AProVE. In: Proceedings of IJCAR’14
Gonnord, L., Monniaux, D., Radanne, G.: Synthesis of ranking functions using extremal counterexamples. In: Proceedings of PLDI’15
Gulwani, S., Tiwari, A.: An abstract domain for analyzing heap-manipulating low-level software. In: Proceedings of CAV’07
Habermehl, P., Iosif, R., Rogalewicz, A., Vojnar, T.: Proving termination of tree manipulating programs. In: Proceedings of ATVA’07
Harris, W.R., Lal, A., Nori, A., Rajamani, S.K.: Alternation for termination. In: Proceedings of SAS’10
Heizmann, M., Hoenicke, J., Leike, J., Podelski, A.: Linear ranking for linear lasso programs. In: Proceedings of ATVA’13
Hensel, J., Giesl, J., Frohn, F., Ströder, T.: Proving termination of programs with bitvector arithmetic by symbolic execution. In: Proceedings of SEFM’16
Iosif, R., Rogalewicz, A.: Automata-based termination proofs. Comput. Inf. 32(4), 739–775 (2013)
Jourdan, J.-H., Laporte, V., Blazy, S., Leroy, X., Pichardie, D.: A formally-verified C static analyzer. In: Proceedings of POPL’15
Kop, C., Nishida, N.: Automatic constrained rewriting induction towards verifying procedural programs. In: Proceedings of APLAS’14
Kop, C., Nishida, N.: Constrained Term Rewriting tooL. In: Proceedings of LPAR’15
Kroening, D., Sharygina, N., Tsitovich, A., Wintersteiger, C. M.: Termination analysis with compositional transition invariants. In: Proceedings of CAV’10
Larraz, D., Oliveras, A., Rodríguez-Carbonell, E., Rubio A.: Proving termination of imperative programs using Max-SMT. In: Proceedings of FMCAD’13
Lattner, C., Adve V.S.: LLVM: A compilation framework for lifelong program analysis & transformation. In: Proceedings of CGO’04
Le, T.C., Qin, S., Chin, W.: Termination and non-termination specification inference. In: Proceedings of PLDI’15
LLVM reference manual. http://llvm.org/docs/LangRef.html
Löwe, S., Mandrykin, M., Wendler, P.: CPAchecker with sequential combination of explicit-value analyses and predicate analyses (competition contribution). In: Proceedings of TACAS’14
Magill, S.: Instrumentation Analysis: An Automated Method for Producing Numeric Abstractions of Heap-Manipulating Programs. Ph.D. thesis, CMU, Pittsburgh, PA (2010). Available at http://www.cs.cmu.edu/~smagill/papers/thesis.pdf
Magill, S., Tsai, M., Lee, P., Tsay, Y.: Automatic numeric abstractions for heap-manipulating programs. In: Proceedings of POPL’10
Miné, A.: The octagon abstract domain. Higher-Order and Symbolic Computation 19(1), 31–100 (2006)
Moy, Y., Marché, C.: Modular inference of subprogram contracts for safety checking. J. Symb. Comput., 45(11), 2010
Nieuwenhuis, R., Oliveras, A., Tinelli, C.: Solving SAT and SAT modulo theories: From an abstract Davis–Putnam–Logemann–Loveland procedure to DPLL(T). J. ACM, 53(6), 2006
Nipkow, T., Paulson, L.C., Wenzel, M.: Isabelle/HOL - A Proof Assistant for Higher-Order Logic. Springer, 2002
O’Hearn, P., Reynolds, J., Yang, H.: Local reasoning about programs that alter data structures. In: Proceedings of CSL’01
http://fxr.watson.org/fxr/source/lib/libsa/strlen.c?v=OPENBSD
Otto, C., Brockschmidt, M., von Essen, C., Giesl, J.: Automated termination analysis of Java Bytecode by term rewriting. In: Proceedings of RTA’10
Podelski, A., Rybalchenko, A.: ARMC: The logical choice for software model checking with abstraction refinement. In: Proceedings of PADL’07
Reps, T.W., Horwitz, S., Sagiv, S.: Precise interprocedural dataflow analysis via graph reachability. In: Proceedings of POPL’95
Spoto, F., Mesnard, F., Payet, É.: A termination analyser for Java Bytecode based on path-length. ACM TOPLAS, 32(3), 2010
Ströder, T., Giesl, J., Brockschmidt, M., Frohn, F., Fuhs, C., Hensel, J., Schneider-Kamp, P.: Proving termination and memory safety for programs with pointer arithmetic. In: Proceedings of IJCAR’14
Ströder, T., Aschermann, C., Frohn, F., Hensel, J., Giesl, J.: AProVE: Termination and memory safety of C programs (competition contribution). In: Proceedings of TACAS’15
Thiemann, R., Sternagel, C.: Certification of termination proofs using CeTA. In: Proceedings of TPHOLs’09
Tsitovich, A., Sharygina, N., Wintersteiger, C.M., Kroening, D.: Loop summarization and termination analysis. In: Proceedings of TACAS’11
Urban, C., Gurfinkel, A., Kahsai, T.: Synthesizing ranking functions from bits and pieces. In: Proceedings of TACAS’16
Wikibooks C Programming. http://en.wikibooks.org/wiki/C_Programming/
Zhao, J., Nagarakatte, S., Martin, M.M.K., Zdancewic, S.: Formalizing the LLVM IR for verified program transformations. In: Proceedings of POPL’12
Author information
Authors and Affiliations
Corresponding author
Additional information
Supported by Deutsche Forschungsgemeinschaft (DFG) Grant GI 274/6-1, Research Training Group 1298 (AlgoSyn), and the Danish Council for Independent Research, Natural Sciences.
Rights and permissions
About this article
Cite this article
Ströder, T., Giesl, J., Brockschmidt, M. et al. Automatically Proving Termination and Memory Safety for Programs with Pointer Arithmetic. J Autom Reasoning 58, 33–65 (2017). https://doi.org/10.1007/s10817-016-9389-x
Received:
Accepted:
Published:
Issue Date:
DOI: https://doi.org/10.1007/s10817-016-9389-x