Lightweight Verification of Array Indexing
Total Page:16
File Type:pdf, Size:1020Kb
Lightweight Verification of Array Indexing Martin Kellogg Vlastimil Dort Suzanne Millstein Michael D. Ernst U. of Washington, USA Charles U., Czechia U. of Washington, USA U. of Washington, USA [email protected] [email protected] [email protected] [email protected] ABSTRACT • prevent the vulnerability rather than merely detecting it at In languages like C, out-of-bounds array accesses lead to security run-time and crashing the program. vulnerabilities and crashes. Even in managed languages like Java, • be efficient, with no time or space overhead at run time. which check array bounds at run time, out-of-bounds accesses cause • be compatible with legacy code, which need not be rewritten. exceptions that terminate the program. • be sound: prevent all bounds errors, rather than finding some We present a lightweight type system that certifies, at compile and letting hackers and users encounter others. time, that array accesses in the program are in-bounds. The type sys- • be precise: issue few false positive warnings. tem consists of several cooperating hierarchies of dependent types, • be fast: work modularly and incrementally when the program- specialized to the domain of array bounds-checking. Programmers mer makes a change. write type annotations at procedure boundaries, allowing modular • be deterministic with regard to output and run time; small pro- verification at a cost that scales linearly with program size. gram changes do not have large or non-local effects. We implemented our type system for Java in a tool called the • be comprehensible: the user can understand why the analysis Index Checker. We evaluated the Index Checker on over 100,000 fails or succeeds, and the output localizes the actual error. lines of open-source code and discovered array access errors even • be effective: finds new bugs in real-world codebases. in well-tested, industrial projects such as Google Guava. • be usable, without disproportionate effort or code clutter. CCS Concepts: • Software and its engineering → Software Many academic and industrial approaches have been put for- verification; Automated static analysis; Data types and struc- ward to address this important problem. These advances have made tures; both theoretical and practical contributions to science and engi- Keywords: Pluggable type systems, Index Checker, Checker Framework neering. Dynamic bounds checking augments the program with ACM Reference Format: run-time checks, crashing the program (i.e., by throwing an excep- Martin Kellogg, Vlastimil Dort, Suzanne Millstein, and Michael D. Ernst. tion) instead of performing illegal operations. This widely adopted 2018. Lightweight Verification of Array Indexing. In Proceedings of 27th approach fails the first three criteria. Heuristic-based, compile-time ACM SIGSOFT International Symposium on Software Testing and Analysis bug-finding tools are useful for finding some defects, but provide no (ISSTA’18). ACM, New York, NY, USA, 12 pages. https://doi.org/10.1145/ guarantee, failing the soundness criterion. Several types of sound 3213846.3213849 static analyses could prevent bounds errors at compile time, though 1 INTRODUCTION most sound tools have not been evaluated in substantive case stud- ies. Proof assistants fail the compatibility, speed, and comprehen- a[i] ≤ i i < ¹aº An array access is in-bounds if 0 and length . sibility criteria: they require heroic effort to use and understand, Unsafe array accesses are a common source of bugs. Their effects and they require re-implementation of the program or the program- include denial of service (via crashes or otherwise), exfiltration of ming language. Automated theorem provers translate the verification sensitive data, and code injection. They are the single most impor- problem into a satisfiability problem, then invoke a solver; they tant cause of security vulnerabilities [41]: buffer overflows enabled fail at comprehensibility and either speed or determinism. Bounded the Morris Worm, SQL Slammer, Code Red, and Heartbleed, among verification (model checking or exhaustive testing) is generally not many others, allowing hackers to, for example, steal 4.5 million sound. Inference approaches do not require programmer annota- medical records [21]. If all array accesses were guaranteed to be in- tions but fail the speed, determinism, and comprehensibility criteria. bounds, these attacks would be impossible. A run-time system can Hybrid static–dynamic approaches trade off the criteria, but sat- prevent out-of-bounds accesses, but at the cost of halting the pro- isfy no more of them than their component approaches. Section7 gram, which is undesirable. Despite decades of research, preventing discusses related work in more detail and gives citations. out-of-bounds accesses remains an urgent, difficult, open problem. We propose to prove safety of bounds checks—equivalently, to An ideal technique for avoid out-of-bounds array accesses should detect all possible erroneous array accesses—via a collection of type satisfy the following criteria: systems. Typechecking is a nonstandard choice for this problem. Permission to make digital or hard copies of all or part of this work for personal or In previous attempts, types were too weak to capture the rich classroom use is granted without fee provided that copies are not made or distributed arithmetic properties required to prove facts about array indexing, for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights for components of this work owned by others than the could be hard to understand, and cluttered the code. One of our author(s) must be honored. Abstracting with credit is permitted. To copy otherwise, or contributions is to show that a carefully-designed collection of type republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Request permissions from [email protected]. systems—each specialized to a simple property—is an excellent fit ISSTA’18, July 16–21, 2018, Amsterdam, Netherlands to the problem. © 2018 Copyright held by the owner/author(s). Publication rights licensed to ACM. A type system can satisfy all the criteria listed above. Typecheck- ACM ISBN 978-1-4503-5699-2/18/07...$15.00 ing happens at compile time. It can run within an existing compiler https://doi.org/10.1145/3213846.3213849 ISSTA’18, July 16–21, 2018, Amsterdam, Netherlands Martin Kellogg, Vlastimil Dort, Suzanne Millstein, and Michael D. Ernst and is familiar to programmers. Our type system is sound (see sec- Constants (§2.4) tion 6.1 for the usual caveats about our implementation). Every i = 3, a:lenдth = 4 sound analysis suffers false positives, but ours issues fewer false positives than Java’s own type system does (table3). Typecheck- ing is modular and deterministic. Types systems specialized to a Linear inequalities Minimum lengths (§2.5) simple property do not require programmers to reason about the (§2.7) i < j a:lenдth > 10 full complexity of dependent types. Our experiments show types are effective for array bounds checking. We hope our success in- spires researchers to consider types as a practical approach in other Negative indices (§2.8) Lower bounds (§2.2) challenging verification domains. jij < a:lenдth i ≥ 0 We have developed a set of lightweight, easy-to-understand type systems, which we implemented in a tool called the Index Checker. The Index Checker provides the strong guarantee that a program is free of out-of-bounds array accesses, without the large human effort Equal lengths (§2.6) Upper bounds (§2.3) typically required for such guarantees. The Index Checker scales to a:lenдth = b:lenдth i < a:lenдth and finds serious bugs in well-tested, industrial-size codebases. The Index Checker’s type systems are simple enough for developers Figure 1: Information flows between type systems. The type sys- to reason about yet rich enough to guarantee that real programs tems with two boxes ensure each array access is safe; the other type systems support the work of these two. A dashed line indicates flow are free of indexing errors (or to reveal subtle errors). The Index of information from user-written annotations (see section 2.9). Checker verifies that programmer-written type annotations are consistent with the code; that is, at run time, the values have the only those two properties would flood the user with false positives. given type. This provides a documentation benefit: programmers One of our contributions is identifying 7 kinds of knowledge (fig.1) cannot forget to write documentation of necessary indexing-related that are adequately precise in practice, and designing abstractions properties, the documentation is guaranteed to be correct, and the (type systems) for each. Each subsequent section gives an example types are both more formal and more concise than informal English of safe code that cannot be typechecked under the analyses shown documentation. so far, and shows how we enhanced our design to accommodate We implemented our type systems for Java. Our work general- that code. These enhancements improve precision without affecting izes to other languages because there is nothing about our type soundness. systems that is specific to Java. Our implementation handles ar- bitrary fixed-length data structures, such as arrays, strings, and 2.1 Background user-defined classes. In fact, it found errors in collection classes defined in Google’s Guava library. A type is a set of run-time values: an expression’s compile-time We evaluated our type system with three case studies on open- type is an overestimate of all its possible run-time values. Type- source code in everyday industrial use. The case studies show checking is a dataflow analysis that produces sound estimates of that the Index Checker scales to practical programs at reasonable what a program may compute. Some of our types apply to integers, programmer effort. The Index Checker found bugs in well-tested, like Java’s int, Integer, etc.