Computer Science Graduate Seminar: Symbolic Execution and Program Synthesis: A General Methodology for Software Veriﬁcation
Monday, January 28, 2019, 4:30pm
Location: Building E3, Seminar room 9222, Ahornstr. 55
Speaker: Thomas Ströder, Dipl.-Inform. (i2)
We are concerned with the correctness of software and present a general methodology for verifying properties of programs in virtually any programming language. This methodology consists of two stages: First, we symbolically execute the program in question to obtain a ﬁnite over-approximation of all possible program executions for all possible inputs. We call this over-approximation a symbolic execution graph. The diﬀerence of this graph compared to most existing approaches using abstract domains is that its construction is strongly guided by the analyzed program rather than using program-independent widening operators. Afterwards, we synthesize programs in simple programming languages from the symbolic execution graph capturing the essence of the program properties that we want to analyze. Thus, the subsequent analysis of these properties on the synthesized programs is substantially simpliﬁed. So we exploit synergies between program analysis and synthesis techniques to obtain powerful veriﬁcation approaches.
Although the over-approximation induced by the symbolic execution graph and subsequent program synthesis might lose precision, our empirical evaluations demonstrate that the abstract domains introduced in this thesis are more precise than other existing domains while still allowing eﬃcient analyses in practice (in particular due to the simpliﬁcation obtained by program synthesis and the exploitation of powerful existing analysis techniques for the simple target languages). Hence, our methodology proved to be very successful in the leading international competitions in the ﬁelds of our analyses.
We apply our methodology to prove determinacy, termination, and upper runtime complexity bounds of Prolog programs, memory safety and termination of LLVM programs (compiled from C programs), deadlock and livelock freedom of concurrent imperative programs with shared memory, and to provide a solution to the Behavior Composition Problem, a program synthesis problem trying to reliably achieve a deterministic target behavior with a set of non-deterministic agents acting in a non-deterministic environment.
Our methodology is particularly useful for analyzing “universal” properties that hold for all program executions (e.g., termination, memory safety, or upper bounds on runtime complexity). Here, our empirical results show that our approach clearly outperforms any previous existing approach. While our methodology can to some extent also be applied to analyze “existential” properties that only need to hold for some program executions (e.g., proving the presence of defects like non-termination or violations of memory safety), additional eﬀort is necessary to make our approach sound for such analyses due to the over-approximation inherently involved. In principle, we can detect candidates satisfying such existential properties in our symbolic execution graph but must prove their feasibility separately.
Many of our contributions have been implemented in the veriﬁcation tool AProVE and empirically evaluated by extensive experiments. Our contributions advance the state of the art in automated program veriﬁcation and oﬀer a guideline for creating future veriﬁcation techniques following our methodology.
The computer science lecturers invite interested people to join.