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

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.
A Program Transformation For Faster Goal-Directed Search Akash Lal, Shaz Qadeer Microsoft Research.
Context-Sensitive Interprocedural Points-to Analysis in the Presence of Function Pointers Presentation by Patrick Kaleem Justin.
Interprocedural Analysis Mooly Sagiv Tel Aviv University Sunday Scrieber 8 Monday
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.
Ch. 8 Functions.
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.
Recap from last time We were trying to do Common Subexpression Elimination Compute expressions that are available at each program point.
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.
Speeding Up Dataflow Analysis Using Flow- Insensitive Pointer Analysis Stephen Adams, Tom Ball, Manuvir Das Sorin Lerner, Mark Seigle Westley Weimer Microsoft.
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?
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://
CSE 451 Section 4 Project 2 Design Considerations.
1 Pointers, Dynamic Data, and Reference Types Review on Pointers Reference Variables Dynamic Memory Allocation –The new operator –The delete operator –Dynamic.
Overview of program analysis Mooly Sagiv html://
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.
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.
Exploiting Buffer Overflows on AIX/PowerPC HP-UX/PA-RISC Solaris/SPARC.
CSE 451 – Operating Systems Section, Autumn 2003 TAs: Mike Swift Adrienne Noble
Unit Testing 101 Black Box v. White Box. Definition of V&V Verification - is the product correct Validation - is it the correct product.
Saturn Overview1 An Overview of the Saturn Project.
David Evans These slides: Introduction to Static Analysis.
Implementing System Calls CS552 Kartik Gopalan. CS552/BU/Spring2008 Steps in writing a system call 1.Create an entry for the system call in the kernel’s.
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.
Using Types to Analyze and Optimize Object-Oriented Programs By: Amer Diwan Presented By: Jess Martin, Noah Wallace, and Will von Rosenberg.
POINTERS Introduction to Systems Programming - COMP 1002, 1402.
PHY 107 – Programming For Science. Today’s Goal  Learn how arrays normally used in real programs  Why a function returning an array causes bugs  How.
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.
Inter-procedural analysis
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.
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
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
Verifying the Safety of User Pointer Dereferences
Hongtao Yu Wei Huo ZhaoQing Zhang XiaoBing Feng
Light-weight Contexts: An OS Abstraction for Safety and Performance
System Calls David Ferry CSCI 3500 – Operating Systems
Language-based Security
Introduction to Static Analyzer
C++ Pointers and Strings
Annotation-Assisted Lightweight Static Checking
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
Presentation transcript:

Verifying the Safety of User Pointer Dereferences Suhabe Bugrara 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 –provide guarantee of correctness Precision –report low number of false alarms Scalability –analyze more than 6 MLOC

Verification

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

Verification Soundness –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 –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 1.Proves the absence of vulnerabilities 2.May report false alarms

Soundness Caveats 1.Unsafe memory operations 2.Concurrency 3.Inline assembly 4.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)

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 (*u,user) (*u,checked) 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 (*u,user) lost precision! (*u,user) (*u,checked) 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 …, but, procedure does not contain any vulnerabilities! (*u,user) (*u,error) emit warning! (*u,user) lost precision! (*u,user) (*u,checked) 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)

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: }

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: }

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: }

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: }

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 (*u,user)  true 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 (*u,user)  true 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: } “guard”

Path Sensitive Analysis (*u,user)  true 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 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error) ... (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 &&... (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && !(cmd == 1) &&... (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && !(cmd == 1) && true... (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  cmd == 1 && !(cmd == 1) && true  false (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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 (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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

Scalability 1.Abstraction –Throw away guards at procedure boundaries 2.Compositionality –Analyze each procedure in isolation

Path Sensitive Analysis (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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

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

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

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

Abstraction (*u,user)  true (*u,checked)  cmd == 1 (*u,error)  false (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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

Abstraction (*u,user)  true (*u,checked)  false (*u,error)  false (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true (*u,checked)  cmd == 1 (*u,user)  true 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

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

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

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

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

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

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

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 1.Alias analysis computes memory model for each procedure

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

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

Linux built for x86 lines of code6.2 million procedures91,543 global variables40,760 composite types14,794 initializers35,317 loops33,886 system call parameters627 dereference sites867,544

Experiment Setup time bound per procedure3 minutes alias analysis time outs~9 K procedures (10%) user ptr analysis time outs154 procedures (0.17%) compute nodes25 cpus per node4 memory per node6 GB total run time3.5 hours

Results Verified automatically –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

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:} False Alarm: Interprocedural Must-Modify 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. *arg checked under condition _SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0 && _SIOC_DIR(cmd)&_SIOC_WRITE 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. *arg checked under condition _SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0 && _SIOC_DIR(cmd)&_SIOC_WRITE 1 2

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. *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 1 2

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 –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