verifying the safety of user pointer dereferences suhabe bugrara [email protected] stanford...
Post on 21-Dec-2015
212 views
TRANSCRIPT
Verifying the Safety of User Pointer Dereferences
Suhabe [email protected]
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 PointerDereferences
1: static ssize_t read_port(…, char * __user buf, …) { 2: unsigned long i = *ppos; 3: char * __user tmp = buf; 4:
Unchecked User PointerDereferences
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)
//deref10: return -EFAULT; 11: i++; 12: tmp++; 13: }14:15: *ppos = i; 16: return tmp-buf; 17: }
Unchecked User PointerDereferences
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) //deref10: 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
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
Example1: 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 Approach1: 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 Approach1: 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 Approach1: 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
(*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)
(*u,user)
(*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)
(*u,user)
(*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)
(*u,user)
(*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
Example1: 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 Analysis1: 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) true1: 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) true1: 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) true1: 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) true1: 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
(*u,user) true(*u,checked) cmd == 1
(*u,user) true1: 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
(*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
(*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) true1: 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
(*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) true1: 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
(*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) true1: 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
(*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) true1: 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
(*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) true1: 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
(*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) true1: 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
(*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) true1: 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
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) true1: 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
Abstraction
(*u,user) true(*u,checked) cmd == 1(*u,error) false
initial summary
Abstraction
(*u,user) true(*u,checked) cmd == 1(*u,error) false
α =
abstraction function
initial summary
Abstraction
(*u,user) true(*u,checked) cmd == 1(*u,error) false
α =(*u,user) true(*u,checked) false(*u,error) false
abstraction function
initial summary
finalsummary
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) true1: 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
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) true1: 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
Compositionality1: int get (int *v) {
2: int x;
3:
4: x = *v;
5:
6: return x;
7: }
Compositionality
(*v,user) c1
1: int get (int *v) {
2: int x;
3:
4: x = *v;
5:
6: return x;
7: }
Compositionality
(*v,user) c1
1: int get (int *v) {
2: int x;
3:
4: x = *v;
5:
6: return x;
7: }
“context variable”
Compositionality
(*v,user) c1
1: int get (int *v) {
2: int x;
3:
4: x = *v;
5:
6: return x;
7: }
(*v,user) c1
Compositionality
(*v,user) c1
1: int get (int *v) {
2: int x;
3:
4: x = *v;
5:
6: return x;
7: }
(*v,user) c1
(*v,user) c1
(*v,error) c1
Compositionality
(*v,user) c1
1: int get (int *v) {
2: int x;
3:
4: x = *v;
5:
6: return x;
7: }
(*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
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 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– 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. *argchecked 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. *argchecked 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. *argchecked 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