Type Qualifiers: Lightweight Specifications to Improve Software
Total Page:16
File Type:pdf, Size:1020Kb
Type Qualifiers: Lightweight Specifications to Improve Software Quality by Jeffrey Scott Foster B.S. (Cornell University) 1995 M.Eng. (Cornell University) 1996 A dissertation submitted in partial satisfaction of the requirements for the degree of Doctor of Philosophy in Computer Science in the GRADUATE DIVISION of the UNIVERSITY OF CALIFORNIA, BERKELEY Committee in charge: Professor Alexander S. Aiken, Chair Professor Susan L. Graham Professor Hendrik W. Lenstra Fall 2002 Type Qualifiers: Lightweight Specifications to Improve Software Quality Copyright 2002 by Jeffrey Scott Foster 1 Abstract Type Qualifiers: Lightweight Specifications to Improve Software Quality by Jeffrey Scott Foster Doctor of Philosophy in Computer Science University of California, Berkeley Professor Alexander S. Aiken, Chair Software plays a pivotal role in our daily lives, yet software glitches and security vulner- abilities continue to plague us. Existing techniques for ensuring the quality of software are limited in scope, suggesting that we need to supply programmers with new tools to make it easier to write programs with fewer bugs. In this dissertation, we propose using type qualifiers, a lightweight, type-based mechanism, to improve the quality of software. In our framework, programmers add a few qualifier annotations to their source code, and type qualifier inference determines the remaining qualifiers and checks consistency of the qualifier annotations. In this dissertation we develop scalable inference algorithms for flow- insensitive qualifiers, which are invariant during execution, and for flow-sensitive qualifiers, which may vary from one program point to the next. The latter inference algorithm in- corporates flow-insensitive alias analysis, effect inference, ideas from linear type systems, and lazy constraint resolution to scale to large programs. We also describe a new language construct “restrict” that allows a programmer to specify certain aliasing properties, and we give a provably sound system for checking usage of restrict. In our system, restrict is used to improve the precision of flow-sensitive type qualifier inference. Finally, we describe a tool for adding type qualifiers to the C programming language, and we present several experiments using our tool, including finding security vulnerabilities in popular C programs and finding deadlocks in the Linux kernel. i To my wife Elise ii Contents List of Figures iv 1 Introduction 1 2 Background 8 2.1 Standard Type Systems . 11 2.2 Standard Type Inference . 15 2.3 Partial Orders and Lattices . 18 3 Flow-Insensitive Type Qualifiers 21 3.1 Qualifiers and Qualified Types . 21 3.2 Qualifier Assertions and Annotations . 24 3.3 Flow-Insensitive Type Qualifier Checking . 25 3.4 Flow-Insensitive Type Qualifier Inference . 28 3.5 Semantics and Soundness . 35 3.6 Subtyping Under Non-Writable Pointer Types . 35 3.7 Related Work . 36 4 Flow-Sensitive Type Qualifiers and Restrict 38 4.1 Designing a Flow-Sensitive Type Qualifier System . 39 4.1.1 Abstract Stores, Abstract Locations, and Linearities . 40 4.1.2 Effects . 43 4.2 Restrict . 44 4.3 Aliasing, Effects, and Restrict . 46 4.3.1 A Flow-Insensitive Checking System . 48 4.3.2 Semantics and Soundness of Restrict . 54 4.3.3 A Flow-Insensitive Inference System . 58 4.3.4 Subsumption on Effects . 64 4.4 Flow-Sensitive Type Qualifier Checking . 65 4.5 Flow-Sensitive Type Qualifier Inference . 71 4.5.1 Flow-Sensitive Constraint Resolution . 77 4.6 Related Work . 83 iii 5 CQual 86 5.1 Syntactic Issues and Partial Order Configuration Files . 87 5.2 Modeling C Types . 90 5.3 Unsafe Features of C . 96 5.4 Presenting Qualifier Inference Results . 100 5.5 Comparison of Restrict to ANSI C . 103 5.6 Related Work . 107 6 Experiments 110 6.1 Const Inference . 110 6.1.1 Experiments . 112 6.2 Format-String Vulnerabilities . 115 6.2.1 Experiments . 117 6.2.2 Related Work . 119 6.3 Linux Kernel Locking . 120 6.3.1 Experiments . 121 6.3.2 Related Work . 127 6.4 File Operations . 127 6.4.1 Experiments . 130 7 Conclusion 131 A Soundness of Flow-Insensitive Type Qualifiers 133 A.1 Small-Step Semantics . 133 A.2 Soundness . 135 B Soundness of Restrict 141 Bibliography 159 iv List of Figures 2.1 Source Language . 9 2.2 Big-Step Operational Semantics . 10 2.3 Big-Step Operational Semantics, Error Rules . 12 2.4 Standard Type Checking System . 13 2.5 Standard Type Inference System . 16 2.6 Type Equality Constraint Resolution . 17 2.7 Two-point Partial Orders . 19 2.8 Three-Point Partial Orders . 19 3.1 Example Qualifier Partial Order . 22 3.2 Subtyping Qualified Types . 23 3.3 Source Language with Qualifier Annotations and Checks . 24 3.4 Definitions of strip(·) and embed(·, ·)...................... 26 3.5 Qualified Type Checking System . 27 3.6 Qualified Type Inference System . 29 3.7 Subtype Constraint Resolution . 30 3.8 Qualifier Constraint Solving . 31 3.9 Big-Step Operational Semantics with Qualifiers . 34 3.10 Subtyping Non-Writable References . 36 4.1 Example Program . 39 4.2 Using Effects at Function Calls . 43 4.3 Source Language and Target Language with Location and Effect Annotations 47 4.4 Alias, Effect, and Restrict Checking . 49 4.5 Translation of Example Program in Figure 4.1 . 53 4.6 New Big-Step Operational Semantics Rules for Restrict . 54 4.7 Alias and Effect Inference and Restrict Checking . 60 4.8 Alias and Effect Constraint Resolution . 61 4.9 Effect Constraint Normal Form . 62 4.10 Solving Effect Constraint System with respect to Location ρ . 63 4.11 Subsumption Rule for Effects . 64 4.12 Flow-Sensitive Qualified Types . 65 4.13 Subtyping and Store Compatibility Rules . 67 v 4.14 Flow-Sensitive Qualified Type Checking System . 68 4.15 ⊕ Operation on Partial Stores . 69 4.16 Extending a Solution to Constructed Stores . 72 4.17 Flow-Sensitive Qualified Type Inference System . 74 4.18 Store Constraints for Example in Figure 4.5 . 76 4.19 Lazy Constraint Propagation . 80 4.20 Lazy Location Propagation Subroutines . 81 4.21 Constraint Resolution for Figure 4.18 . 82 5.1 Cqual System Architecture . 87 5.2 Partial Order Configuration File Grammar . 89 5.3 Example Partial Order Configuration File . 90 5.4 Example Partial Order Configuration File (continued) . 91 5.5 Sample Run of Cqual .............................. 101 6.1 Const Subtyping with Pointers . 112 6.2 Const Inference Results . 113 6.3 Graph of Const Inference Results . 114 6.4 Program with a Format-String Vulnerability . 116 6.5 Format-String Vulnerability Detection Benchmarks . 118 6.6 Format-String Vulnerability Detection Results . 119 6.7 Running Time for Whole Module Analysis of Locks . 126 6.8 Memory Usage for Whole Module Analysis of Locks . 126 6.9 Subtyping Relation among C Stream Library Qualifiers . 128 A.1 Small-Step Operational Semantics with Qualifiers . 134 B.1 Complete Big-Step Operational Semantics for Restrict . 142 B.2 Error Rules for Restrict . 142 vi Acknowledgements First and foremost, I would like to thank my advisor Alex Aiken for his advice, support, wisdom, and unrelenting optimism, all of which have benefited me tremendously the past six and a half years. I would also like to thank the current and former members of my research group, Manuel F¨ahndrich, Zhendong Su, David Gay, Ben Liblit, Tachio Terauchi, and John Kodumal. Their questions, answers, and camaraderie have been a source of inspiration I have come to rely on. I am also indebted to Martin Elsman, Umesh Shankar, Kunal Talwar, and David Wagner, my additional collaborators on some of this work. Finally, I would like to thank my other committee members, Susan Graham and Hendrik Lenstra, for their helpful advice and feedback. 1 Chapter 1 Introduction Software systems are an increasingly important part of our daily lives. Today, everything from routine office transactions to critical infrastructure services relies on the effectiveness of large, complicated software systems, yet our ability to produce such systems far outstrips our ability to ensure their quality. Well-publicized software glitches have led to failures such as the Mars Climate Orbiter crash [76], and security vulnerabilities in software have paved the way for attacks such as the Code Red Worm [15]. The potentially staggering cost of software quality problems has led to a renewed call to increase the safety, reliability, and maintainability of software [49, 89]. There are currently two widely used techniques for validating program properties: testing and code auditing. In testing, either programmers or testers check their program on a series of inputs designed to exercise the system’s various features. In code auditing, groups of programmers manually review source code together, looking for potential problems. Both techniques are extremely useful in practice: testing catches many errors and shows that the code runs correctly on a variety of inputs, and code auditing can find both obvious and subtle bugs in software. Unfortunately, while effective, both techniques have serious limitations. Although well-designed test cases cover much of the behavior of a program, the only assurance testing provides is that the test cases work correctly. Code auditing is extremely difficult, and it is unreasonable to assume that manual inspection can show the safety of a large, complicated software system. Given these limitations, it seems clear that we need to complement both testing and code auditing with other techniques. In this dissertation we propose using type qualifiers to improve software quality. Type qualifiers are lightweight annotations for specifying program properties (see below). 2 In later chapters we present techniques.