How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem,...

23
How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I s about how to find lots of error s in real coe. The way we are going to do this is a bit unusual. Rather than undertand what rules the systemmust follow or swhat state it is in, both of which are hard. Instead we are going to do omething much easier; we will infer what rules it believes it must obey and what state it cbelieve it is in and cross check thee believes for contradictions. The great thing this buys us is that now we can find lots of errors in coe we do no t unederstand.
  • date post

    19-Dec-2015
  • Category

    Documents

  • view

    223
  • download

    0

Transcript of How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem,...

Page 1: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

How to find lots of bugs by checking program belief systems

Dawson EnglerDavid Chen, Seth Hallem, Ben Chelf, Andy

ChouStanford University

This talk I s about how to find lots of error s in real coe. The way we are going to do this is a bit unusual. Rather than undertand what rules the systemmust follow or swhat state it is in, both of which are hard. Instead we are going to do omething much easier; we will infer what rules it believes it must obey and what state it cbelieve it is in and cross check thee believes for contradictions. The great thing this buys us is that now we can find lots of errors in coe we do no t unederstand.

This talk I s about how to find lots of error s in real coe. The way we are going to do this is a bit unusual. Rather than undertand what rules the systemmust follow or swhat state it is in, both of which are hard. Instead we are going to do omething much easier; we will infer what rules it believes it must obey and what state it cbelieve it is in and cross check thee believes for contradictions. The great thing this buys us is that now we can find lots of errors in coe we do no t unederstand.

Page 2: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Systems have many ad hoc correctness rules– “acquire lock l before modifying x”, “cli() must be

paired with sti(),” “don’t block with interrupts disabled”

– One error = crashed machine If we know rules, can check with extended

compiler – Rules map to simple source constructs– Use compiler extensions to express them

– Nice: scales, precise, statically find 1000s of errors

Context: finding OS bugs w/ compilers

Reduced to using grep on millions of line of code, or documentation, hoping you can find all cases

Reduced to using grep on millions of line of code, or documentation, hoping you can find all cases

lock_kernel();if (!de->count) { printk("free!\n"); return;} unlock_kernel();

Linuxfs/proc/inode.c

GNU C compiler

Lock checker “missingunlock!”

Page 3: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Problem: what are the rules?!?!– 100-1000s of rules in 100-1000s of subsystems. – To check, must answer: Must a() follow b()? Can

foo() fail? Does bar(p) free p? Does lock l protect x?

– Manually finding rules is hard. So don’t. Instead infer what code believes, cross check for contradiction

Intuition: how to find errors without knowing truth?– Contradiction. To find lies: cross-examine. Any

contradiction is an error.– Deviance. To infer correct behavior: if 1 person

does X, might be right or a coincidence. If 1000s do X and 1 does Y, probably an error.

– Crucial: we know contradiction is an error without knowing the correct belief!

Goal: find as many serious bugs as possible

Reduced to using grep on millions of line of code, or documentation, hoping you can find all cases

Reduced to using grep on millions of line of code, or documentation, hoping you can find all cases

Page 4: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

MUST beliefs: – Inferred from acts that imply beliefs code *must*

have.

– Check using internal consistency: infer beliefs at different locations, then cross-check for contradiction

MAY beliefs: could be coincidental – Inferred from acts that imply beliefs code *may*

have

– Check as MUST beliefs; rank errors by belief confidence.

Cross-checking program belief systems

x = *p / z; // MUST belief: p not null // MUST: z != 0unlock(l); // MUST: l acquiredx++; // MUST: x not protected by l

// MAY: A() and B()// must be paired

Specification = checkable redundancy. Can cross check code against itself for same effect. Others: that

x was not already equal to value.

Specification = checkable redundancy. Can cross check code against itself for same effect. Others: that

x was not already equal to value.

B(); // MUST: B() need not // be preceded by A()

A();…B();

A();…B();

A();…B();

A();…B();

Page 5: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Trivial consistency: NULL pointers *p implies MUST belief:

– p is not null A check (p == NULL) implies two MUST beliefs:

– POST: p is null on true path, not null on false path– PRE: p was unknown before check

Cross-check these for three different error types. Check-then-use (79 errors, 26 false pos)

/* 2.4.1: drivers/isdn/svmb1/capidrv.c */if(!card) printk(KERN_ERR, “capidrv-%d: …”, card->contrnr…)

Show because it is one of the simplest possible checkers, and because it finds hundreds of errors.

Show because it is one of the simplest possible checkers, and because it finds hundreds of errors.

Page 6: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Null pointer fun Use-then-check: 102 bugs, 4 false

Contradiction/redundant checks (24 bugs, 10 false)

/* 2.4.7: drivers/char/mxser.c */struct mxser_struct *info = tty->driver_data;unsigned flags;if(!tty || !info->xmit_buf)

return 0;

/* 2.4.7/drivers/video/tdfxfb.c */ fb_info.regbase_virt = ioremap_nocache(...); if(!fb_info.regbase_virt) return -ENXIO; fb_info.bufbase_virt = ioremap_nocache(...); /* [META: meant fb_info.bufbase_virt!] */ if(!fb_info.regbase_virt) { iounmap(fb_info.regbase_virt);

Can look for redundancy in general: deadcode elim is an error finder. Can look for: writes never read, lock acquired that protects nothing,

Can look for redundancy in general: deadcode elim is an error finder. Can look for: writes never read, lock acquired that protects nothing,

Page 7: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Redundancy checking Assume: code supposed to be useful

– Useless actions = conceptual confusion. Like type systems, high level bugs map to low-level redundancies

Identity operations: “x = x”, “1 * y”, “x & x”, “x | x”

Assignments that are never read:

/* 2.4.5-ac8/net/appletalk/aarp.c */ da.s_node = sa.s_node; da.s_net = da.s_net;

for(entry=priv->lec_arp_tables[i];entry != NULL; entry=next){

next = entry->next; if (…) lec_arp_remove(priv->lec_arp_tables, entry); lec_arp_unlock(priv); return 0;}

Can look for redundancy in general: deadcode elim is an error finder. Can look for: writes never read, lock acquired that protects nothing. Redundant transition means we’re missing something with analysis.

Can look for redundancy in general: deadcode elim is an error finder. Can look for: writes never read, lock acquired that protects nothing. Redundant transition means we’re missing something with analysis.

Critical sections that have no shared state, contradictory booleans – in general look at deadcode elim and CSE as error signalers

Critical sections that have no shared state, contradictory booleans – in general look at deadcode elim and CSE as error signalers

Page 8: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Internal Consistency: finding security holes Applications are bad:

– Rule: “do not dereference user pointer <p>”– One violation = security hole– Detect with static analysis if we knew which were

“bad”– Big Problem: which are the user pointers???

Sol’n: forall pointers, cross-check two OS beliefs– “*p” implies safe kernel pointer– “copyin(p)/copyout(p)” implies dangerous user

pointer– Error: pointer p has both beliefs.– Implemented as a two pass global checker

Result: 24 security bugs in Linux, 18 in OpenBSD– (about 1 bug to 1 false positive)

First pass: mark all pointers treated as user pointers. Second pass: make sure they are never dereferenced.

First pass: mark all pointers treated as user pointers. Second pass: make sure they are never dereferenced.

Page 9: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Still alive in linux 2.4.4:

– Tainting marks “rt” as a tainted pointer, checking warns that rt is passed to a routine that dereferences it

– 2 other examples in same routine…

/* drivers/net/appletalk/ipddp.c:ipddp_ioctl */case SIOCADDIPDDPRT: return ipddp_create(rt);case SIOCDELIPDDPRT: return ipddp_delete(rt);case SIOFCINDIPDDPRT:

if(copy_to_user(rt, ipddp_find_route(rt), sizeof(struct ipddp_route)))

return –EFAULT;

An exampleMarked as tainted because passed as the first argument to copy_to_user, which is used

to access potentientially bad user pointers. Does global analysis to detect that the pointer will be dereferenced by ippd_…

Marked as tainted because passed as the first argument to copy_to_user, which is used to access potentientially bad user pointers. Does global analysis to detect that the

pointer will be dereferenced by ippd_…

Page 10: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Common: multiple implementations of same interface.– Beliefs of one implementation can be checked

against those of the others! User pointer (3 errors):

If one implementation taints its argument, all others must

– How to tell? Routines assigned to same function pointer

– More general: infer execution context, arg preconditions…

– Interesting q: what spec properties can be inferred?

Cross checking beliefs related abstractly

–Parameter features: Can a param be null? What are legal values of integer parameter Return code: What are allowable error code to return & when? Execution context: Are interrupts off or on when code runs? When it exits? Does it run concurrently?

–Parameter features: Can a param be null? What are legal values of integer parameter Return code: What are allowable error code to return & when? Execution context: Are interrupts off or on when code runs? When it exits? Does it run concurrently?

foo_write(void *p, void *arg,…){ copy_from_user(p, arg, 4); disable(); … do something … enable(); return 0;}

bar_write(void *p, void *arg,…){ *p = *(int *)arg; … do something … disable(); return 0;}If one does it right, we can cross check all: if

one dev gets it right we are in great shape.

If one does it right, we can cross check all: if one dev gets it right we are in great shape.

Page 11: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Handling MAY beliefs MUST beliefs: only need a single contradiction MAY beliefs: need many examples to separate

fact from coincidence Conceptually:

– Assume MAY beliefs are MUST beliefs– Record every successful check with a “check”

message– Every unsuccessful check with an “error”

message– Use the test statistic to rank errors based on ratio

of checks (n) to errors (err)

– Intuition: the most likely errors are those where n is large, and err is small.

z(n, err) = ((n-err)/n-p0)/sqrt(p0*(1-p0)/n)

Page 12: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Statistical: Deriving deallocation routines Use-after free errors are horrible.

– Problem: lots of undocumented sub-system free functions

– Soln: derive behaviorally: pointer “p” not used after call “foo(p)” implies MAY belief that “foo” is a free function

Conceptually: Assume all functions free all arguments– (in reality: filter functions that have suggestive

names)– Emit a “check” message at every call site.– Emit an “error” message at every use

– Rank errors using z test statistic: z(checks, errors)– E.g., foo.z(3, 3) < bar.z(3, 1) so rank bar’s error first – Results: 23 free errors, 11 false positives

foo(p);*p = x;

foo(p);*p = x;

foo(p);*p = x;

bar(p);p = 0;

bar(p);p = 0;

bar(p);*p = x;

Can cross-correlate: free is on error path, has dealloc in name, etc, bump up ranking. Foo has 3 errors, and 3 checks. Bar, 3 checks, one error. Essentially every passed check implies belief held, every error =

not held

Can cross-correlate: free is on error path, has dealloc in name, etc, bump up ranking. Foo has 3 errors, and 3 checks. Bar, 3 checks, one error. Essentially every passed check implies belief held, every error =

not held

Page 13: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

A bad free error

/* drivers/block/cciss.c:cciss_ioctl */ if (iocommand.Direction == XFER_WRITE){

if (copy_to_user(...)) { cmd_free(NULL, c); if (buff != NULL) kfree(buff); return( -EFAULT); } } if (iocommand.Direction == XFER_READ) { if (copy_to_user(...)) { cmd_free(NULL, c); kfree(buff); } } cmd_free(NULL, c); if (buff != NULL) kfree(buff);

Page 14: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Traditional: – Use global analysis to track which routines return

NULL– Problem: false positives when pre-conditions hold,

difficult to tell statically (“return p->next”?) Instead: see how often programmer checks.

– Rank errors based on number of checks to non-checks. Algorithm: Assume *all* functions can return NULL

– If pointer checked before use, emit “check” message– If pointer used before check, emit “error”

– Sort errors based on ratio of checks to errors Result: 152 bugs, 16 false.

Statistical: deriving routines that can fail

P = foo(…);*p = x;

p = bar(…);If(!p) return;*p = x;

p = bar(…);If(!p) return;*p = x;

p = bar(…);If(!p) return;*p = x;

p = bar(…);*p = x;

Can also use consistency: if a routine calls a routine that fails, then it to can fail. Similarly, if a routine checks foo for failure, but calls bar, which does not, is a type error. (In a sense can use witnesses: take good code and see what it does, reapply to unknown code)

Can also use consistency: if a routine calls a routine that fails, then it to can fail. Similarly, if a routine checks foo for failure, but calls bar, which does not, is a type error. (In a sense can use witnesses: take good code and see what it does, reapply to unknown code)

Page 15: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

The worst bug Starts with weird way of checking failure:

So why are we looking for “seg_alloc”?/* ipc/shm.c:750:newseg: */if (!(shp = seg_alloc(...)) return -ENOMEM;id = shm_addid(shp);

/* 2.3.99: ipc/shm.c:1745:map_zero_setup */if (IS_ERR(shp = seg_alloc(...))) return PTR_ERR(shp);

static inline long IS_ERR(const void *ptr) { return (unsigned long)ptr > (unsigned long)-1000L; }

int ipc_addid(…* new…) { ... new->cuid = new->uid =…; new->gid = new->cgid = … ids->entries[id].p = new;

Page 16: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Deriving “A() must be followed by B()” “a(); … b();” implies MAY belief that a() follows b()

– Programmer may believe a-b paired, or might be a coincidence.

Algorithm:– Assume every a-b is a valid pair (reality: prefilter

functions that seem to be plausibly paired)– Emit “check” for each path that has a() then b()– Emit “error” for each path that has a() and no b()

– Rank errors for each pair using the test statistic» z(foo.check, foo.error) = z(2, 1)

Results: 23 errors, 11 false positives.

foo(p, …) bar(p, …);

“check foo-bar”

x();y();

“check x-y”

foo(p, …);…

“error:foo, no bar!”

Page 17: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Checking derived lock functions Evilest:

And the award for best effort:

/* 2.4.1: drivers/sound/trident.c: trident_release: lock_kernel(); card = state->card; dmabuf = &state->dmabuf; VALIDATE_STATE(state);

/* 2.4.0:drivers/sound/cmpci.c:cm_midi_release: */ lock_kernel(); if (file->f_mode & FMODE_WRITE) { add_wait_queue(&s->midi.owait, &wait); ... if (file->f_flags & O_NONBLOCK) { remove_wait_queue(&s->midi.owait, &wait); set_current_state(TASK_RUNNING); return –EBUSY; … unlock_kernel();

Page 18: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Key ideas: – Check code beliefs: find errors without knowing

truth.– Beliefs code MUST have: Contradictions = errors– Beliefs code MAY have: check as MUST beliefs and

rank errors by belief confidence

Secondary ideas:– Check for errors by flagging redundancy.– Analyze client code to infer abstract features

rather than just implementation.– Spec = checkable redundancy. Can use code for

same.

Summary: Belief Analysis

Page 19: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Example free checker

sm free_checker { state decl any_pointer v; decl any_pointer x; start: { kfree(v); } v.freed ; v.freed: { v == x } | { v != x } { /* suppress fp */ } | { v } { err(“Use after free!”); ;}

start

v.freed

error

use(v)

kfree(v)

Simple. Have had freshman write these and post bugs to linux groups.

Three parts: start state. Pattern; raw c coede or wildcards., match does a transition, callouts. Scales with sophistication of analysis.

System will kill variable, track when assigned to others.

Simple. Have had freshman write these and post bugs to linux groups.

Three parts: start state. Pattern; raw c coede or wildcards., match does a transition, callouts. Scales with sophistication of analysis.

System will kill variable, track when assigned to others.

Page 20: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Example inferring free checker

sm free_checker { state decl any_pointer v; decl any_pointer x; decl any_fn_call call; decl any_args args; start: { call(v) } { char *n = mc_identifier(call); if(strstr(n, “free”) || strstr(n, “dealloc”) || … ) { mc_v_set_state(v, freed); mc_v_set_data(v, n); note(“NOTE: %s”, n); } }; v.freed: { v == x } | { v != x } { /* suppress fp */ } | { v } { err(“Use after free %s!”, mc_v_get_data(v)); ;

Simple. Have had freshman write these and post bugs to linux groups.

Three parts: start state. Pattern, match does a transition, callouts. Scales with sophistication of analysis.

Simple. Have had freshman write these and post bugs to linux groups.

Three parts: start state. Pattern, match does a transition, callouts. Scales with sophistication of analysis.

Page 21: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Systems software has many ad-hoc restrictions:– “reenable disabled interrupts,” “hold lock l before

using x”– Lots of rules; one bug = crashed system

Our approach: system-specific extensions check rules

– Scalable: handles millions of lines of code – Precise: says exactly what error was – Static: does not require running code– Effective: 1500+ errors in Linux source code

Context: finding OS bugs w/ static analysis

lock_kernel();if (!de->count) { printk("free!\n"); return;} unlock_kernel();

Linuxfs/proc/inode.c

GNU C compiler

Lock checker “missingunlock!”

Follow paths in code to see that they satisfy a partial order, or do not overcommit resources, or obey temporal orderings: key abstract rules map pretty clearly to source code.

Follow paths in code to see that they satisfy a partial order, or do not overcommit resources, or obey temporal orderings: key abstract rules map pretty clearly to source code.

Page 22: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

*THE* problem: what are the rules?!?!– 100-1000s of rules in 100-1000s of subsystems. – To check, must answer: Must a() follow b()? Can

foo() fail? Does bar(p) free p? Does lock l protect x?

– Manually finding rules is hard. So don’t. Instead infer what code believes, cross check for contradiction

Intuition: how to find errors without knowing truth?– Contradiction. To find lies: cross-examine. Any

contradiction is an error.– Deviance. To infer correct behavior: if 1 person

does X, might be right or a coincidence. If 1000s do X and 1 does Y, probably an error.

– Crucial: we know contradiction is an error without knowing the correct belief!

Goal: find as many serious bugs as possible.

Reduced to using grep on millions of line of code, or documentation, hoping you can find all cases

Reduced to using grep on millions of line of code, or documentation, hoping you can find all cases

Page 23: How to find lots of bugs by checking program belief systems Dawson Engler David Chen, Seth Hallem, Ben Chelf, Andy Chou Stanford University This talk I.

Tool-based checking– PREfix/PREfast– Slam– ESP, ASTlog

Higher level languages– TypeState, Vault– Foster et al’s type qualifier work.

Derivation:– Houdini to infer ESC specs– Daikon for dynamic invariants– Larus et al dynamic temporal inference

Spec extraction– Bandera– Slam

Related work