dynamic symbolic execution

92
Dynamic Symbolic Execution CS 6340 1

Upload: nina-barlow

Post on 31-Dec-2015

72 views

Category:

Documents


3 download

DESCRIPTION

Dynamic Symbolic Execution. CS 6340. Today, QA is mostly testing. “50% of my company employees are testers, and the rest spends 50% of their time testing!” Bill Gates 1995. Software Reliability Importance. Software is pervading every aspects of life Software everywhere - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Dynamic Symbolic Execution

Dynamic Symbolic Execution

CS 6340

1

Page 2: Dynamic Symbolic Execution

2

Today, QA is mostly testing “50% of my company employees are testers,

and the rest spends 50% of their time testing!”

Bill Gates 1995

Page 3: Dynamic Symbolic Execution

3

Software Reliability Importance Software is pervading every aspects of life

Software everywhere Software failures were estimated to cost the US economy

about $60 billion annually [NIST 2002] Improvements in software testing infrastructure may save one-

third of this cost Testing accounts for an estimated 50%-80% of the cost of

software development [Beizer 1990]

Page 4: Dynamic Symbolic Execution

4

Big PictureSafe Programming

Languages and Type systems

Static Program Analysis

Dynamic Program Analysis

Model Based Software Development and Analysis

Model Checking

and Theorem Proving

Runtime Monitorin

g

Testing

Page 5: Dynamic Symbolic Execution

5

Big PictureSafe Programming

Languages and Type systems

Model Checking

and Theorem Proving

Runtime Monitorin

g

Model Based Software Development and Analysis

Static Program Analysis

Dynamic Program Analysis

Testing

Page 6: Dynamic Symbolic Execution

6

A Familiar Program: QuickSortvoid quicksort (int[] a, int lo, int hi) {

int i=lo, j=hi, h; int x=a[(lo+hi)/2];

// partition do {

while (a[i]<x) i++; while (a[j]>x) j--; if (i<=j) {

h=a[i]; a[i]=a[j]; a[j]=h; i++; j--;

} } while (i<=j);

// recursion if (lo<j) quicksort(a, lo, j); if (i<hi) quicksort(a, i, hi);

}

Page 7: Dynamic Symbolic Execution

7

A Familiar Program: QuickSortvoid quicksort (int[] a, int lo, int hi) {

int i=lo, j=hi, h; int x=a[(lo+hi)/2];

// partition do {

while (a[i]<x) i++; while (a[j]>x) j--; if (i<=j) {

h=a[i]; a[i]=a[j]; a[j]=h; i++; j--;

} } while (i<=j);

// recursion if (lo<j) quicksort(a, lo, j); if (i<hi) quicksort(a, i, hi);

}

Test QuickSort Create an array Initialize the elements of the array Execute the program on this array

How much confidence do I have in this testing method?

Is my test suite *Complete*? Can someone generate a small and *Complete* test

suite for me?

Page 8: Dynamic Symbolic Execution

8

Automated Test Generation Studied since 70’s

King 76, Myers 79 30 years have passed, and yet no effective

solution What Happened?

Page 9: Dynamic Symbolic Execution

9

Automated Test Generation Studied since 70’s

King 76, Myers 79 30 years have passed, and yet no effective

solution What Happened?

Program-analysis techniques were expensive Automated theorem proving and constraint

solving techniques were not efficient

Page 10: Dynamic Symbolic Execution

10

Automated Test Generation Studied since 70’s

King 76, Myers 79 30 years have passed, and yet no effective

solution What Happened?

Program-analysis techniques were expensive Automated theorem proving and constraint

solving techniques were not efficient Recent years have seen remarkable strides

in static analysis and constraint solving SLAM, BLAST, ESP, Bandera, Saturn, MAGIC

Page 11: Dynamic Symbolic Execution

11

Automated Test Generation Studied since 70’s

King 76, Myers 79 30 years have passed, and yet no effective

solution What Happened?

Program analysis techniques were expensive Automated theorem proving and constraint

solving techniques were not efficient Recent years have seen remarkable strides

in static analysis and constraint solving SLAM, BLAST, ESP, Bandera, Saturn, MAGIC

Question: Can we use similar techniques in Automated

Testing?

Page 12: Dynamic Symbolic Execution

12

Systematic Automated Testing

Testing

Static Analysis

Automated Theorem Proving

Model-Checking and

Formal Methods

Constraint Solving

Page 13: Dynamic Symbolic Execution

13

CUTE and DART Combine random testing (concrete execution) and

symbolic testing (symbolic execution) [PLDI’05, FSE’05, FASE’06, CAV’06,ISSTA’07, ICSE’07]

Concrete + Symbolic = Concolic

Page 14: Dynamic Symbolic Execution

14

Goal Automated Unit Testing of real-world C and

Java Programs Generate test inputs Execute unit under test on generated test inputs

so that all reachable statements are executed Any assertion violation gets caught

Page 15: Dynamic Symbolic Execution

15

Goal Automated Unit Testing of real-world C and

Java Programs Generate test inputs Execute unit under test on generated test inputs

so that all reachable statements are executed Any assertion violation gets caught

Our Approach: Explore all execution paths of an Unit for all

possible inputs Exploring all execution paths ensure that all reachable

statements are executed

Page 16: Dynamic Symbolic Execution

16

Execution Paths of a Program Can be seen as a binary

tree with possibly infinite depth Computation tree

Each node represents the execution of a “if then else” statement

Each edge represents the execution of a sequence of non-conditional statements

Each path in the tree represents an equivalence class of inputs

0 1

0 0

0

0

1

1

1

1

1

1

Page 17: Dynamic Symbolic Execution

17

Example of Computation Tree

void test_me(int x, int y) { if(2*x==y){

if(x != y+10){ printf(“I am fine here”);} else { printf(“I should not reach here”); ERROR;}

}}

2*x==y

x!=y+10

N Y

N Y

ERROR

Page 18: Dynamic Symbolic Execution

18

Concolic Testing: Finding Security and Safety Bugs

Divide by 0 Error

x = 3 / i;

Buffer Overflow

a[i] = 4;

Page 19: Dynamic Symbolic Execution

19

Concolic Testing: Finding Security and Safety Bugs

Divide by 0 Error

if (i !=0)

x = 3 / i;

else

ERROR;

Buffer Overflow

if (0<=i && i < a.length)

a[i] = 4;

else

ERROR;

Key: Add Checks Automatically and

Perform Concolic Testing

Page 20: Dynamic Symbolic Execution

20

Existing Approach I Random testing

generate random inputs execute the program on generated inputs

Probability of reaching an error can be astronomically less

test_me(int x){

if(x==94389){

ERROR;

}

}

Probability of hitting ERROR = 1/232

Page 21: Dynamic Symbolic Execution

21

Existing Approach II Symbolic Execution

use symbolic values for input variables execute the program symbolically on symbolic input values collect symbolic path constraints use theorem prover to check if a branch can be taken

Does not scale for large programs

test_me(int x) {if ((x%10)*4!=17) {

ERROR;} else {

ERROR;}

}

Symbolic execution will say both branches are reachable:

False positive

Page 22: Dynamic Symbolic Execution

22

Existing Approach II Symbolic Execution

use symbolic values for input variables execute the program symbolically on symbolic input values collect symbolic path constraints use theorem prover to check if a branch can be taken

Does not scale for large programs

test_me(int x) {if (bbox(x)!=17) {

ERROR;} else {

ERROR;}

}

Symbolic execution will say both branches are reachable:

False positive

Page 23: Dynamic Symbolic Execution

23

Concolic Testing Approach

Random Test Driver: random values for x and y

Probability of reaching ERROR is extremely low

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Page 24: Dynamic Symbolic Execution

24

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7 x = x0, y = y0

Page 25: Dynamic Symbolic Execution

25

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7, z = 14

x = x0, y = y0, z = 2*y0

Page 26: Dynamic Symbolic Execution

26

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7, z = 14

x = x0, y = y0, z = 2*y0

2*y0 != x0

Page 27: Dynamic Symbolic Execution

27

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

2*y0 != x0

Solve: 2*y0 == x0

Solution: x0 = 2, y0 = 1

x = 22, y = 7, z = 14

x = x0, y = y0, z = 2*y0

Page 28: Dynamic Symbolic Execution

28

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 2, y = 1 x = x0, y = y0

Page 29: Dynamic Symbolic Execution

29

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 2, y = 1, z = 2

x = x0, y = y0, z = 2*y0

Page 30: Dynamic Symbolic Execution

30

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 2, y = 1, z = 2

x = x0, y = y0, z = 2*y0

2*y0 == x0

Page 31: Dynamic Symbolic Execution

31

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 2, y = 1, z = 2

x = x0, y = y0, z = 2*y0

2*y0 == x0

x0 <= y0+10

Page 32: Dynamic Symbolic Execution

32

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 2, y = 1, z = 2

x = x0, y = y0, z = 2*y0

Solve: (2*y0 == x0) and (x0 > y0 + 10)

Solution: x0 = 30, y0 = 15

2*y0 == x0

x0 <= y0+10

Page 33: Dynamic Symbolic Execution

33

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 30, y = 15 x = x0, y = y0

Page 34: Dynamic Symbolic Execution

34

Concolic Testing Approach

int double (int v) {

return 2*v; }

void testme (int x, int y) {

z = double (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 30, y = 15 x = x0, y = y0

2*y0 == x0

x0 > y0+10

Program Error

Page 35: Dynamic Symbolic Execution

35

Explicit Path (not State) Model Checking Traverse all execution

paths one by one to detect errors assertion violations program crash uncaught exceptions

combine with valgrind to discover memory errors

F T

F F

F

F

T

T

T

T

T

T

Page 36: Dynamic Symbolic Execution

36

Explicit Path (not State) Model Checking Traverse all execution paths one by one to detect

errors assertion violations program crash uncaught exceptions

combine with valgrind to discover memory errors

F T

F F

F

F

T

T

T

T

T

T

Page 37: Dynamic Symbolic Execution

37

Explicit Path (not State) Model Checking Traverse all execution paths one by one to detect

errors assertion violations program crash uncaught exceptions

combine with valgrind to discover memory errors

F T

F F

F

F

T

T

T

T

T

T

Page 38: Dynamic Symbolic Execution

38

Explicit Path (not State) Model Checking Traverse all execution paths one by one to detect

errors assertion violations program crash uncaught exceptions

combine with valgrind to discover memory errors

F T

F F

F

F

T

T

T

T

T

T

Page 39: Dynamic Symbolic Execution

39

Explicit Path (not State) Model Checking Traverse all execution paths one by one to detect

errors assertion violations program crash uncaught exceptions

combine with valgrind to discover memory errors

F T

F F

F

F

T

T

T

T

T

T

Page 40: Dynamic Symbolic Execution

40

Explicit Path (not State) Model Checking Traverse all execution paths one by one to detect

errors assertion violations program crash uncaught exceptions

combine with valgrind to discover memory errors

F T

F F

F

F

T

T

T

T

T

T

Page 41: Dynamic Symbolic Execution

41

Novelty: Simultaneous Concrete and Symbolic Execution

int foo (int v) {

return (v*v) % 50; }

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7 x = x0, y = y0

Page 42: Dynamic Symbolic Execution

42

int foo (int v) {

return (v*v) % 50; }

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7, z = 49

x = x0, y = y0, z = (y0*y0)%50

(y0*y0)%50 !=x0

Solve: (y0*y0 )%50 == x0

Don’t know how to solve!

Stuck?

Novelty: Simultaneous Concrete and Symbolic Execution

Page 43: Dynamic Symbolic Execution

43

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7, z = 49

x = x0, y = y0, z = foo (y0)

foo (y0) !=x0

Novelty: Simultaneous Concrete and Symbolic Execution

Solve: foo (y0) == x0

Don’t know how to solve!

Stuck?

Page 44: Dynamic Symbolic Execution

44

int foo (int v) {

return (v*v) % 50; }

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7, z = 49

x = x0, y = y0, z = (y0*y0)%50

(y0*y0)%50 !=x0

Solve: (y0*y0 )%50 == x0

Don’t know how to solve!

Not Stuck!

Use concrete state

Replace y0 by 7 (sound)

Novelty: Simultaneous Concrete and Symbolic Execution

Page 45: Dynamic Symbolic Execution

45

int foo (int v) {

return (v*v) % 50; }

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 22, y = 7, z = 48

x = x0, y = y0, z = 49

49 !=x0

Solve: 49 == x0

Solution : x0 = 49, y0 = 7

Novelty: Simultaneous Concrete and Symbolic Execution

Page 46: Dynamic Symbolic Execution

46

int foo (int v) {

return (v*v) % 50; }

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 49, y = 7 x = x0, y = y0

Novelty: Simultaneous Concrete and Symbolic Execution

Page 47: Dynamic Symbolic Execution

47

int foo (int v) {

return (v*v) % 50; }

void testme (int x, int y) {

z = foo (y);

if (z == x) {

if (x > y+10) {

ERROR;}

}

}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

path condition

x = 49, y = 7, z = 49

x = x0, y = y0 ,

z = 49

2*y0 == x0

x0 > y0+10

Program Error

Novelty: Simultaneous Concrete and Symbolic Execution

Page 48: Dynamic Symbolic Execution

48

Concolic Testing: A Middle Approach

+ Complex programs+ Efficient- Less coverage+ No false positive

- Simple programs- Not efficient+ High coverage- False positive

Random Testing

Symbolic Testing

Concolic Testing

+ Complex programs

+/- Somewhat efficient

+ High coverage

+ No false positive

Page 49: Dynamic Symbolic Execution

49

Implementations DART and CUTE for C programs jCUTE for Java programs

Goto http://srl.cs.berkeley.edu/~ksen/ for CUTE and jCUTE binaries

MSR has four implementations SAGE, PEX, YOGI, Vigilante

Similar tool: EXE at Stanford Easiest way to use and to develop on top of

CUTE Implement concolic testing yourself

Page 50: Dynamic Symbolic Execution

50

Another Example: Testing Data Structurestypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

• Random Test Driver:

• random memory graph reachable from p

• random value for x

• Probability of reaching abort( ) is extremely low

Page 51: Dynamic Symbolic Execution

51

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0

p , x=236

NULL

Page 52: Dynamic Symbolic Execution

52

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0

p , x=236

NULL

Page 53: Dynamic Symbolic Execution

53

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p=p0, x=x0

p , x=236

NULL

Page 54: Dynamic Symbolic Execution

54

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p=p0, x=x0

p , x=236

NULL

!(p0!=NULL)

Page 55: Dynamic Symbolic Execution

55

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p=p0, x=x0

p , x=236

NULL

p0=NULL

solve: x0>0 and p0NULL

Page 56: Dynamic Symbolic Execution

56

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p=p0, x=x0

p , x=236

NULL

p0=NULL

solve: x0>0 and p0NULL

x0=236, p0

634

NULL

Page 57: Dynamic Symbolic Execution

57

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

Page 58: Dynamic Symbolic Execution

58

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

x0>0

Page 59: Dynamic Symbolic Execution

59

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

x0>0

p0NULL

Page 60: Dynamic Symbolic Execution

60

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

x0>0

p0NULL

2x0+1v0

Page 61: Dynamic Symbolic Execution

61

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

x0>0

p0NULL

2x0+1v0

Page 62: Dynamic Symbolic Execution

62

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

x0>0

p0NULL

2x0+1v0

solve: x0>0 and p0NULL and 2x0+1=v0

Page 63: Dynamic Symbolic Execution

63

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=236

NULL

634

x0>0

p0NULL

2x0+1v0

solve: x0>0 and p0NULL and 2x0+1=v0

x0=1, p0

3

NULL

Page 64: Dynamic Symbolic Execution

64

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

Page 65: Dynamic Symbolic Execution

65

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

Page 66: Dynamic Symbolic Execution

66

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

p0NULL

Page 67: Dynamic Symbolic Execution

67

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

p0NULL

2x0+1=v0

Page 68: Dynamic Symbolic Execution

68

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

p0NULL

2x0+1=v0

n0p0

Page 69: Dynamic Symbolic Execution

69

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

p0NULL

2x0+1=v0

n0p0

Page 70: Dynamic Symbolic Execution

70

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

solve: x0>0 and p0NULL and 2x0+1=v0 and n0=p0

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

p0NULL

2x0+1=v0

n0p0

Page 71: Dynamic Symbolic Execution

71

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

solve: x0>0 and p0NULL and 2x0+1=v0 and n0=p0

x0=1, p0

3

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

NULL

3

x0>0

p0NULL

2x0+1=v0

n0p0

Page 72: Dynamic Symbolic Execution

72

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

3

Page 73: Dynamic Symbolic Execution

73

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

3

Page 74: Dynamic Symbolic Execution

74

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p0NULLp=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

3

Page 75: Dynamic Symbolic Execution

75

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p0NULL

2x0+1=v0

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

3

Page 76: Dynamic Symbolic Execution

76

CUTE Approachtypedef struct cell { int v; struct cell *next;} cell;

int f(int v) { return 2*v + 1;}

int testme(cell *p, int x) { if (x > 0) if (p != NULL) if (f(x) == p->v) if (p->next == p) abort(); return 0;}

Concrete Execution

Symbolic Execution

concrete state

symbolic state

constraints

x0>0

p0NULL

2x0+1=v0

n0=p0

p=p0, x=x0,p->v=v0,

p->next=n0

p , x=1

3

Program Error

Page 77: Dynamic Symbolic Execution

78

CUTE in a Nutshell Generate concrete inputs one by one

each input leads program along a different path

Page 78: Dynamic Symbolic Execution

79

CUTE in a Nutshell Generate concrete inputs one by one

each input leads program along a different path On each input execute program both concretely and

symbolically

Page 79: Dynamic Symbolic Execution

80

CUTE in a Nutshell Generate concrete inputs one by one

each input leads program along a different path On each input execute program both concretely and

symbolically Both cooperate with each other

concrete execution guides the symbolic execution

Page 80: Dynamic Symbolic Execution

81

CUTE in a Nutshell Generate concrete inputs one by one

each input leads program along a different path On each input execute program both concretely and

symbolically Both cooperate with each other

concrete execution guides the symbolic execution concrete execution enables symbolic execution to

overcome incompleteness of theorem prover replace symbolic expressions by concrete values if

symbolic expressions become complex resolve aliases for pointer using concrete values handle arrays naturally

Page 81: Dynamic Symbolic Execution

82

CUTE in a Nutshell Generate concrete inputs one by one

each input leads program along a different path On each input execute program both concretely and

symbolically Both cooperate with each other

concrete execution guides the symbolic execution concrete execution enables symbolic execution to

overcome incompleteness of theorem prover replace symbolic expressions by concrete values if

symbolic expressions become complex resolve aliases for pointer using concrete values handle arrays naturally

symbolic execution helps to generate concrete input for next execution increases coverage

Page 82: Dynamic Symbolic Execution

83

Instrumentation

Page 83: Dynamic Symbolic Execution

84

Instrumented Program Maintains a symbolic state

maps each memory location used by the program to symbolic expressions over symbolic input variables

Maintains a path constraint a sequence of constraints C1, C2,…, Ck

such that Ci s are symbolic constraints over symbolic input variables

C1 ∧ C2 ∧ … ∧ Ck holds over the path currently executed by the program

Page 84: Dynamic Symbolic Execution

85

Instrumented Program Maintains a symbolic state

maps each memory location used by the program to symbolic expressions over symbolic input variables

Maintains a path constraint a sequence of constraints C1, C2,…, Ck

such that Ci s are symbolic constraints over symbolic input variables

C1 ∧ C2 ∧ … ∧ Ck holds over the path currently executed by the program

Arithmetic Expressionsa1x1+…+anxn+c

Pointer Expressionsx

Page 85: Dynamic Symbolic Execution

86

Instrumented Program Maintains a symbolic state

maps each memory location used by the program to symbolic expressions over symbolic input variables

Maintains a path constraint a sequence of constraints C1, C2,…, Ck

such that Ci s are symbolic constraints over symbolic input variables

C1 ∧ C2 ∧ … ∧ Ck holds over the path currently executed by the program

Arithmetic Expressionsa1x1+…+anxn+c

Pointer Expressionsx

Arithmetic Constraintsa1x1+…+anxn ¸ 0

Pointer Constraintsx=y

x yx=NULL

x NULL

Page 86: Dynamic Symbolic Execution

87

Constraint SolvingPath constraint

C1 ∧ C2 ∧ … ∧ Ck ∧ … ∧ Cn

Page 87: Dynamic Symbolic Execution

88

Constraint SolvingPath constraint

C1 ∧ C2 ∧ … ∧ Ck ∧ … ∧ Cn

Solve

C1 ∧C2 ∧ … ∧ !Ck

to generate next input

Page 88: Dynamic Symbolic Execution

89

Constraint SolvingPath constraint

C1 ∧ C2 ∧ … ∧ Ck ∧ … ∧ Cn

Solve

C1 ∧C2 ∧ … ∧ !Ck

to generate next input

If Ck is pointer constraint then invoke pointer solver

Ci1 ∧ C

i2 ∧ … ∧ !Ck where ij in [1..k-1] and C

ij is

pointer constraint

If Ck is arithmetic constraint then invoke linear solver

Ci1 ∧ C

i2 ∧ … ∧ !Ck where ij in [1..k-1] and C

ij is

arithmetic constraint

Page 89: Dynamic Symbolic Execution

90

SGLIB: popular library for C data-structures Used in Xrefactory a commercial tool for refactoring

C/C++ programs Found two bugs in sglib 1.0.1

reported them to authors fixed in sglib 1.0.2

Bug 1: doubly-linked list library

segmentation fault occurs when a non-zero length list is concatenated with a zero-length list

discovered in 140 iterations ( < 1second) Bug 2:

hash-table an infinite loop in hash table is member function 193 iterations (1 second)

Page 90: Dynamic Symbolic Execution

91

SGLIB: popular library for C data-structures

NameRun time

in seconds

# of Iterations

# of BranchesExplored

% Branch Coverage

# of Functions

Tested

# of BugsFound

Array Quick Sort 2 732 43 97.73 2 0

Array Heap Sort 4 1764 36 100.00 2 0

Linked List 2 570 100 96.15 12 0

Sorted List 2 1020 110 96.49 11 0

Doubly Linked List 3 1317 224 99.12 17 1

Hash Table 1 193 46 85.19 8 1

Red Black Tree 2629 1,000,000 242 71.18 17 0

Page 91: Dynamic Symbolic Execution

92

Experiments: Needham-Schroeder Protocol Tested a C implementation of a security

protocol (Needham-Schroeder) with a known attack 600 lines of code Took less than 13 seconds on a machine with

1.8GHz Xeon processor and 2 GB RAM to discover middle-man attack

In contrast, a software model-checker (VeriSoft) took 8 hours

Page 92: Dynamic Symbolic Execution

93

Testing Data-structures of CUTE itself Unit tested several non-standard data-

structures implemented for the CUTE tool cu_depend (used to determine dependency

during constraint solving using graph algorithm) cu_linear (linear symbolic expressions) cu_pointer (pointer symbolic expresiions)

Discovered a few memory leaks and a copule of segmentation faults these errors did not show up in other uses of

CUTE for memory leaks we used CUTE in conjunction

with Valgrind