Verifying the Safety of User Pointer Dereferences

Slides:



Advertisements
Similar presentations
Dataflow Analysis for Datarace-Free Programs (ESOP 11) Arnab De Joint work with Deepak DSouza and Rupesh Nasre Indian Institute of Science, Bangalore.
Advertisements

Static Analysis for Security
Christo Wilson Project 2: User Programs in Pintos
Automated Verification with HIP and SLEEK Asankhaya Sharma.
R4 Dynamically loading processes. Overview R4 is closely related to R3, much of what you have written for R3 applies to R4 In R3, we executed procedures.
Abstraction and Modular Reasoning for the Verification of Software Corina Pasareanu NASA Ames Research Center.
Analysis of programs with pointers. Simple example What are the dependences in this program? Problem: just looking at variable names will not give you.
Data-Flow Analysis Framework Domain – What kind of solution is the analysis looking for? Ex. Variables have not yet been defined – Algorithm assigns a.
SPLINT STATIC CHECKING TOOL Sripriya Subramanian 10/29/2002.
Automatic Predicate Abstraction of C-Programs T. Ball, R. Majumdar T. Millstein, S. Rajamani.
Constraint-Based Analysis CS void f(state *x, state *y) { result = spin_trylock( & x->lock); spin_lock( & y->lock); … if (!result) spin_unlock(
Using Programmer-Written Compiler Extensions to Catch Security Holes Authors: Ken Ashcraft and Dawson Engler Presented by : Hong Chen CS590F 2/7/2007.
Saturn1 Scalable Program Analysis Using Boolean Satisfiability: The Saturn Project Alex Aiken Stanford University.
Scalable Error Detection using Boolean Satisfiability 1 Yichen Xie and Alex Aiken Stanford University.
Program analysis Mooly Sagiv html://
Interprocedural analyses and optimizations. Costs of procedure calls Up until now, we treated calls conservatively: –make the flow function for call nodes.
Program analysis Mooly Sagiv html://
Program Analysis for Security Suhabe Bugrara Stanford University.
Software Reliability Methods Sorin Lerner. Software reliability methods: issues What are the issues?
Verifying the Safety of User Pointer Dereferences Suhabe Bugrara Stanford University Joint work with Alex Aiken.
From last time S1: l := new Cons p := l S2: t := new Cons *p := t p := t l p S1 l p tS2 l p S1 t S2 l t S1 p S2 l t S1 p S2 l t S1 p L2 l t S1 p S2 l t.
Overview of program analysis Mooly Sagiv html://
1 Pointers, Dynamic Data, and Reference Types Review on Pointers Reference Variables Dynamic Memory Allocation –The new operator –The delete operator –Dynamic.
Symbolic Path Simulation in Path-Sensitive Dataflow Analysis Hari Hampapuram Jason Yue Yang Manuvir Das Center for Software Excellence (CSE) Microsoft.
Automated Tools for Software Reliability Suhabe Bugrara Stanford University.
Statically Detecting Likely Buffer Overflow Vulnerabilities David Larochelle David Evans University of Virginia Department of Computer Science Supported.
Procedure Optimizations and Interprocedural Analysis Chapter 15, 19 Mooly Sagiv.
CSC 107 – Programming For Science. Today’s Goal  Learn how arrays normally used in real programs  Why a function returning an array causes bugs  How.
Towards Scalable Modular Checking of User-defined Properties Thomas Ball, MSR Brian Hackett, Mozilla Shuvendu Lahiri, MSR Shaz Qadeer, MSR Julien Vanegue,
PRESTO: Program Analyses and Software Tools Research Group, Ohio State University STATIC ANALYSES FOR JAVA IN THE PRESENCE OF DISTRIBUTED COMPONENTS AND.
Introduction Overview Static analysis Memory analysis Kernel integrity checking Implementation and evaluation Limitations and future work Conclusions.
Software Engineering Prof. Dr. Bertrand Meyer March 2007 – June 2007 Chair of Software Engineering Static program checking and verification Slides: Based.
CSE 451 – Operating Systems Section, Autumn 2003 TAs: Mike Swift Adrienne Noble
Saturn Overview1 An Overview of the Saturn Project.
David Evans These slides: Introduction to Static Analysis.
1 Splint: A Static Memory Leakage tool Presented By: Krishna Balasubramanian.
Lecture 6 C++ Programming Arne Kutzner Hanyang University / Seoul Korea.
Dataflow Analysis for Concurrent Programs using Datarace Detection Ravi Chugh, Jan W. Voung, Ranjit Jhala, Sorin Lerner LBA Reading Group Michelle Goodstein.
Genesis: From Raw Hardware to Processes Andy Wang Operating Systems COP 4610 / CGS 5765.
Kernel Exercise 5 Brandon Cline Stuart Fischer Rachel Langhans Brian Minter Adam Stasio.
SATURN: An Overview Shrawan Kumar
/ PSWLAB Evidence-Based Analysis and Inferring Preconditions for Bug Detection By D. Brand, M. Buss, V. C. Sreedhar published in ICSM 2007.
Null Dereference Verification Via Over-approximated Weakest Precondition analysis Ravichandhran Madhavan Microsoft Research, India Joint work with Raghavan.
Program Analysis for Security Original slides created by Prof. John Mitchell.
Jeremy Nimmer, page 1 Automatic Generation of Program Specifications Jeremy Nimmer MIT Lab for Computer Science Joint work with.
Overview Working directly with memory locations is beneficial. In C, pointers allow you to: change values passed as arguments to functions work directly.
Content Coverity Static Analysis Use cases of Coverity Examples
Using SMGCPA for the Detection of Memory Safety Bugs in the Linux Kernel Anton Vasilyev.
Path-Based Fault Correlations
Protection and OS Structure
Protection of System Resources
APEx: Automated Inference of Error Specifications for C APIs
Seminar in automatic tools for analyzing programs with dynamic memory
Ik-Soon Kim December 18, 2010 Embedded Software Platform Team
High Coverage Detection of Input-Related Security Faults
Program Slicing Baishakhi Ray University of Virginia
Hongtao Yu Wei Huo ZhaoQing Zhang XiaoBing Feng
Pointers, Dynamic Data, and Reference Types
Light-weight Contexts: An OS Abstraction for Safety and Performance
CSE 451: Operating Systems Spring 2012 Module 6 Review of Processes, Kernel Threads, User-Level Threads Ed Lazowska 570 Allen.
Language-based Security
Introduction to Static Analyzer
C++ Pointers and Strings
Amir Kamil and Katherine Yelick
CSC-682 Advanced Computer Security
Annotation-Assisted Lightweight Static Checking
Foundations and Definitions
Verification with Small and Short Worlds Rohit Sinha, Cynthia Sturton, Petros Maniatis, Sanjit A. Seshia, David Wagner Introduction Verification of large.
C++ Pointers and Strings
CS444/544 Operating Systems II System Calls and Page Fault
Presentation transcript:

Verifying the Safety of User Pointer Dereferences Suhabe Bugrara suhabe@stanford.edu Stanford University Joint work with Alex Aiken

Unchecked User Pointer Dereferences Security property of operating systems Two types of pointers in operating systems kernel pointer: pointer created by the operating system user pointer: pointer created by a user application and passed to the operating system via an entry point such as a system call Must check that a user pointer points into userspace before dereferencing it

Unchecked User Pointer Dereferences 1: static ssize_t read_port(…, char * __user buf, …) { 2: unsigned long i = *ppos; 3: char * __user tmp = buf; 4:

Unchecked User Pointer Dereferences 1: static ssize_t read_port(…, char * __user buf, …) { 2: unsigned long i = *ppos; 3: char * __user tmp = buf; 4: 7: 8: while (count-- > 0 && i < 65536) { 9: if (__put_user(inb(i),tmp) < 0) //deref 10: return -EFAULT; 11: i++; 12: tmp++; 13: } 14: 15: *ppos = i; 16: return tmp-buf; 17: }

Unchecked User Pointer Dereferences 1: static ssize_t read_port(…, char * __user buf, …) { 2: unsigned long i = *ppos; 3: char * __user tmp = buf; 4: 5: if (!access_ok(..,buf,...)) //check 6: return -EFAULT; 7: 8: while (count-- > 0 && i < 65536) { 9: if (__put_user(inb(i),tmp) < 0) //deref 10: return -EFAULT; 11: i++; 12: tmp++; 13: } 14: 15: *ppos = i; 16: return tmp-buf; 17: }

Security Vulnerability Malicious user could Take control of the operating system Overwrite kernel data structures Read sensitive data out of kernel memory Crash machine by corrupting data

Goal Design a program analysis to prove statically that no unchecked user pointer dereferences exist in the entire operating system

Challenges Verification Precision Scalability provide guarantee of correctness Precision report low number of false alarms Scalability analyze more than 6 MLOC

Verification

Verification Soundness If the program analysis reports that no vulnerabilities exist, then the program contains none

Verification Soundness Completeness If the program analysis reports that no vulnerabilities exist, then the program contains none Completeness If the program analysis reports that a vulnerability exists, then program contains one

Verification Soundness Completeness If the program analysis reports that no vulnerabilities exist, then the program contains none Completeness If the program analysis reports that a vulnerability exists, then program contains one Impossible for a program analysis to be both sound and complete

Sound and Incomplete Verifier Proves the absence of vulnerabilities May report false alarms

Soundness Caveats Unsafe memory operations Concurrency Inline assembly Analysis fails to analyze some procedures

Precision Minimize the number of false alarms Reasoning more deeply about program Computationally expensive High precision inhibits scalability

Example 1: void sys_call (int *u, const int cmd) { //u is user pointer 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { //check u 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; //dereference u 12: }

One Possible Approach 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)

One Possible Approach 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user) (*u,user)

One Possible Approach 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user) (*u,user) (*u,user)

One Possible Approach 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user) (*u,user) (*u,user) (*u,user) (*u,checked)

One Possible Approach 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user) (*u,user) (*u,user) (*u,user) (*u,checked) (*u,user) lost precision!

One Possible Approach 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user) (*u,user) (*u,user) (*u,user) (*u,checked) (*u,user) lost precision! (*u,user) (*u,error) emit warning! …, but, procedure does not contain any vulnerabilities!

Path Sensitivity Ability to reason about branch correlations Programs use substantial amount of branch correlation in practice Important for reducing the number of false alarms

Example 1: void sys_call (int *u, int cmd) { //u is user pointer 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { //check u 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; //dereference u 12: }

Path Sensitivity Valid Path 1: void sys_call (int *u, int cmd) { //u is user pointer 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { //check u 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; //dereference u 12: } Valid Path

Path Sensitivity Valid Path 1: void sys_call (int *u, const int cmd) { //u is user pointer 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { //check u 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; //dereference u 12: } Valid Path

Path Sensitivity Valid Path 1: void sys_call (int *u, const int cmd) { //u is user pointer 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { //check u 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; //dereference u 12: } Valid Path

Path Sensitivity Invalid Path! 1: void sys_call (int *u, const int cmd) { //u is user pointer 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { //check u 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; //dereference u 12: } Invalid Path!

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: }

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true

Path Sensitive Analysis “guard” 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  . . .

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && . . .

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && !(cmd == 1) && . . .

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && !(cmd == 1) && true . . .

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && !(cmd == 1) && true  false

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false

Scalability Abstraction Compositionality Throw away guards at procedure boundaries Compositionality Analyze each procedure in isolation

Path Sensitive Analysis 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false

Abstraction initial summary (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false initial summary

α = Abstraction abstraction function initial summary (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false abstraction function initial summary

α = Abstraction abstraction function initial summary final summary (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false (*u,user)  true (*u,checked)  false (*u,error)  false abstraction function initial summary final summary

Abstraction 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false

Abstraction 1: void sys_call (int *u, const int cmd) { 2: int x; 3: 4: if (cmd == 1) { 5: if (!access_ok(u)) { 6: return; 7: } 8: } 9: … 10: if (cmd == 1) 11: x = *u; 12: } (*u,user)  true (*u,user)  true (*u,user)  true (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  false (*u,error)  false

Compositionality 1: int get (int *v) { 2: int x; 3: 4: x = *v; 5: 6: return x; 7: }

Compositionality 1: int get (int *v) { 2: int x; (*v,user)  c1 3: 5: 6: return x; 7: } (*v,user)  c1

Compositionality “context variable” 1: int get (int *v) { 2: int x; 3: 4: x = *v; 5: 6: return x; 7: } (*v,user)  c1

Compositionality 1: int get (int *v) { 2: int x; (*v,user)  c1 3: 5: 6: return x; 7: } (*v,user)  c1 (*v,user)  c1

Compositionality 1: int get (int *v) { 2: int x; (*v,user)  c1 3: 5: 6: return x; 7: } (*v,user)  c1 (*v,user)  c1 (*v,user)  c1 (*v,error)  c1

Compositionality 1: int get (int *v) { 2: int x; (*v,user)  c1 3: 5: 6: return x; 7: } (*v,user)  c1 (*v,user)  c1 (*v,user)  c1 (*v,error)  c1

Fixed Point Computation Generate summary of behavior for each procedure with respect to calling context Apply summary of callee at call site in caller Repeatedly generate and apply summaries until a fixed point is reached

Analysis Passes Alias analysis computes memory model for each procedure

Analysis Passes Alias analysis User state propagation computes memory model for each procedure User state propagation propagates user states throughout OS

Analysis Passes Alias analysis User state propagation computes memory model for each procedure User state propagation propagates user states throughout OS Unchecked and safety state propagation determines safety of each dereference site

Linux 2.6.17.1 built for x86 lines of code 6.2 million procedures 91,543 global variables 40,760 composite types 14,794 initializers 35,317 loops 33,886 system call parameters 627 dereference sites 867,544

Experiment Setup time bound per procedure 3 minutes alias analysis time outs ~9 K procedures (10%) user ptr analysis time outs 154 procedures (0.17%) compute nodes 25 cpus per node 4 memory per node 6 GB total run time 3.5 hours

Results Verified automatically Warnings 616 out of 627 system call parameters (98.2 %) 851,686 out of 852,092 dereferences (99.95%) Warnings 11 warnings on system call parameters 406 warnings on dereferences 22 annotations required to verify

False Alarm: Interprocedural Must-Modify 1: int verify_iovec (struct msghdr *m, ..., char *address, int mode) 2: { 3: int err; 4: 5: if (m->msg_namelen) { 6: if (mode == VERIFY_READ) { 7: err = move_addr_to_kernel (m->msg_name, 8: m->msg_namelen, 9: address); 10: if (err < 0) return err; 11: } 12: 13: m->msg_name = address; 14: } else { 15: m->msg_name = NULL; 16: } 17: ... 18: }

False Alarm: Interprocedural Must-Modify 1: int verify_iovec (struct msghdr *m, ..., char *address, int mode) 2: { 3: int err; 4: 5: if (m->msg_namelen) { 6: if (mode == VERIFY_READ) { 7: err = move_addr_to_kernel (m->msg_name, 8: m->msg_namelen, 9: address); 10: if (err < 0) return err; 11: } 12: 13: m->msg_name = address; 14: } else { 15: m->msg_name = NULL; 16: } 17: ... 18: }

False Alarm: Interprocedural Must-Modify 1: int verify_iovec (struct msghdr *m, ..., char *address, int mode) 2: { 3: int err; 4: 5: if (m->msg_namelen) { 6: if (mode == VERIFY_READ) { 7: err = move_addr_to_kernel (m->msg_name, 8: m->msg_namelen, 9: address); 10: if (err < 0) return err; 11: } 12: 13: m->msg_name = address; 14: } else { 15: m->msg_name = NULL; 16: } 17: ... 18: }

False Alarm: Interprocedural Must-Modify 1: int verify_iovec (struct msghdr *m, ..., char *address, int mode) 2: { 3: int err; 4: 5: if (m->msg_namelen) { 6: if (mode == VERIFY_READ) { 7: err = move_addr_to_kernel (m->msg_name, 8: m->msg_namelen, 9: address); 10: if (err < 0) return err; 11: } 12: 13: m->msg_name = address; 14: } else { 15: m->msg_name = NULL; 16: } 17: ... 18: } m->msg_name must-modified under !(m->msg_namelen && mode == VERIFY_READ && err < 0)

False Alarm: Interprocedural Branch Correlation 1: int sound_ioctl(uint cmd, ulong arg) { 2: 3: if (_SIOC_DIR(cmd) != _SIOC_NONE && 4: _SIOC_DIR(cmd) != 0) 5: 6: if(_SIOC_DIR(cmd)&_SIOC_WRITE) 7: if (!access_ok(arg)) 8: return -EFAULT; 9: 10: ... 11: return sound_mixer_ioctl(cmd, arg); 12: } 13: int sound_mixer_ioctl(uint cmd, void *arg) 14: { 15: ... 16: return aci_mixer_ioctl(cmd, arg); 17: } 18: 19: 20: int aci_mixer_ioctl(uint cmd, void *arg) 21: { 22: switch(cmd) 23: case SOUND_MIXER_WRITE_IGAIN: 24: ...*arg...; 25: ... 26: }

False Alarm: Interprocedural Branch Correlation 1: int sound_ioctl(uint cmd, ulong arg) { 2: 3: if (_SIOC_DIR(cmd) != _SIOC_NONE && 4: _SIOC_DIR(cmd) != 0) 5: 6: if(_SIOC_DIR(cmd)&_SIOC_WRITE) 7: if (!access_ok(arg)) 8: return -EFAULT; 9: 10: ... 11: return sound_mixer_ioctl(cmd, arg); 12: } 13: int sound_mixer_ioctl(uint cmd, void *arg) 14: { 15: ... 16: return aci_mixer_ioctl(cmd, arg); 17: } 18: 19: 20: int aci_mixer_ioctl(uint cmd, void *arg) 21: { 22: switch(cmd) 23: case SOUND_MIXER_WRITE_IGAIN: 24: ...*arg...; 25: ... 26: } 1

False Alarm: Interprocedural Branch Correlation 1: int sound_ioctl(uint cmd, ulong arg) { 2: 3: if (_SIOC_DIR(cmd) != _SIOC_NONE && 4: _SIOC_DIR(cmd) != 0) 5: 6: if(_SIOC_DIR(cmd)&_SIOC_WRITE) 7: if (!access_ok(arg)) 8: return -EFAULT; 9: 10: ... 11: return sound_mixer_ioctl(cmd, arg); 12: } 13: int sound_mixer_ioctl(uint cmd, void *arg) 14: { 15: ... 16: return aci_mixer_ioctl(cmd, arg); 17: } 18: 19: 20: int aci_mixer_ioctl(uint cmd, void *arg) 21: { 22: switch(cmd) 23: case SOUND_MIXER_WRITE_IGAIN: 24: ...*arg...; 25: ... 26: } 1 1. *arg checked under condition _SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0 && _SIOC_DIR(cmd)&_SIOC_WRITE

False Alarm: Interprocedural Branch Correlation 1: int sound_ioctl(uint cmd, ulong arg) { 2: 3: if (_SIOC_DIR(cmd) != _SIOC_NONE && 4: _SIOC_DIR(cmd) != 0) 5: 6: if(_SIOC_DIR(cmd)&_SIOC_WRITE) 7: if (!access_ok(arg)) 8: return -EFAULT; 9: 10: ... 11: return sound_mixer_ioctl(cmd, arg); 12: } 13: int sound_mixer_ioctl(uint cmd, void *arg) 14: { 15: ... 16: return aci_mixer_ioctl(cmd, arg); 17: } 18: 19: 20: int aci_mixer_ioctl(uint cmd, void *arg) 21: { 22: switch(cmd) 23: case SOUND_MIXER_WRITE_IGAIN: 24: ...*arg...; 25: ... 26: } 1 2 1. *arg checked under condition _SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0 && _SIOC_DIR(cmd)&_SIOC_WRITE

False Alarm: Interprocedural Branch Correlation 1: int sound_ioctl(uint cmd, ulong arg) { 2: 3: if (_SIOC_DIR(cmd) != _SIOC_NONE && 4: _SIOC_DIR(cmd) != 0) 5: 6: if(_SIOC_DIR(cmd)&_SIOC_WRITE) 7: if (!access_ok(arg)) 8: return -EFAULT; 9: 10: ... 11: return sound_mixer_ioctl(cmd, arg); 12: } 13: int sound_mixer_ioctl(uint cmd, void *arg) 14: { 15: ... 16: return aci_mixer_ioctl(cmd, arg); 17: } 18: 19: 20: int aci_mixer_ioctl(uint cmd, void *arg) 21: { 22: switch(cmd) 23: case SOUND_MIXER_WRITE_IGAIN: 24: ...*arg...; 25: ... 26: } 1 2 1. *arg checked under condition _SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0 && _SIOC_DIR(cmd)&_SIOC_WRITE 2. cmd == SOUND_MIXER_WRITE_IGAIN implies _SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0 && _SIOC_DIR(cmd)&_SIOC_WRITE

False Alarm: Function Pointers 1: struct { char *name; ...} map[] = ..., 2: {[NFSCTL_GETFD] = {.name = ".getfd", ...}, 3: [NFSCTL_GETFS] = {.name = ".getfs", ...},}; 4: 5: long sys_nfsservctl (int cmd, ..., void *res) { 6: ... 7: struct file *file = do_open(map[cmd].name); 8: ... 9: int err = file->f_op->read(file, res, ...); 10: ... 11: }

False Alarm: Function Pointers 1: int notifier_call_chain(struct notifier_block **nl, ulong val, void *v) 2: { 3: int ret = NOTIFY_DONE; 4: struct notifier_block *nb; 5: 6: nb = *nl; 7: 8: while (nb) { 9: ret = nb->notifier_call(nb, val, v); 10: ... 11: nb = nb->next; 12: } 13: 14: return ret; 15: }

Related Work MECA, by Yang, Kremenek, Xie, Engler Sparse, by Torvalds bug finder, path-insensitive, Linux, automatic Sparse, by Torvalds bug finder, path-insensitive, Linux, 10,000 annotations CQual, by Johnson, Wagner verifier, path-insensitive, Linux, automatic, 300 KLOC ESP, by Dor, Adams, Das, Yang verifier, path-sensitive, Windows, automatic, 1 MLOC

Future Work Eliminate the time outs on procedures Handle inline assembly statements Reduce number of false alarms

Conclusions Nearly verifying important security property Scaling to largest open source program Reporting low number of false alarms

Questions