Exceptional situations and program reliability
Authors: George C. Necula
Venue:  (2005)

It is difficult to write programs that behave correctly in the presence of run-time errors. Proper behavior in the face of exceptional situations is important to the reliability of long-running programs. Existing programming language features often provide poor support for executing clean-up code and for restoring invariants. We present a dataflow analysis for finding a certain class of mistakes made during exceptional situations. We also present a specification miner for automatically inferring partial notions of what programs should be doing. Finally, we propose and evaluate a new language feature, the compensation stack, to make it easier to write solid code in the presence of run-time errors. We give a dataflow analysis for finding a certain class of exception-handling mistakes: those that arise from a failure to release resources or to clean up properly along all paths. Many real-world programs violate such resource usage rules because of incorrect exception handling. Our flow-sensitive analysis keeps track of outstanding obligations along program paths and does a precise modeling of control flow in the presence of exceptions. Using it, we have found over 800 exception handling mistakes in almost 4 million lines of Java code. The analysis is unsound and produces false positives, but a few simple filtering rules suffice to remove them in practice. The remaining mistakes were manually verified. These mistakes cause sockets, files and database handles to be leaked along some paths. Specifications are necessary in order to find software bugs using program verification tools. We give a novel automatic specification mining algorithm that uses information about exception handling to learn temporal safety rules. Our algorithm is based on the observation that programs often make mistakes along exceptional control-flow paths, even when they behave correctly on normal execution paths. We show that this focus improves the miner's effectiveness at discovering specifications beneficial for bug finding. We present quantitative results comparing our technique to four existing miners. We highlight assumptions made by various miners that are not always borne out in practice. Additionally, we apply our algorithm to existing Java programs and analyze its ability to learn specifications that find bugs in those programs. In our experiments, we find filtering candidate specifications to be more important than ranking them. We find 430 bugs in 1 million lines of code. Notably, we find 250 more bugs using per-program specifications learned by our algorithm than with generic specifications that apply to all programs. We present a characterization of the most common causes of those bugs and discuss the limitations of exception handling, finalizers and destructors. Based on that characterization we propose a programming language feature, the compensation stack, that keeps track of obligations at run time and ensures that they are discharged. Finally, we present case studies to demonstrate that this feature is natural, efficient, and can improve reliability; for example, retrofitting a 34,000-line program with compensation stacks resulted in a 0.5% code size decrease, a surprising 17% speed increase (from correctly deallocating resources in the presence of exceptions), and more consistent behavior.