Presentation is loading. Please wait.

Presentation is loading. Please wait.

Dynamic Symbolic Execution CS 8803 FPL Oct 31, 2012 (Slides adapted from Koushik Sen) 1.

Similar presentations


Presentation on theme: "Dynamic Symbolic Execution CS 8803 FPL Oct 31, 2012 (Slides adapted from Koushik Sen) 1."— Presentation transcript:

1 Dynamic Symbolic Execution CS 8803 FPL Oct 31, 2012 (Slides adapted from Koushik Sen) 1

2 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

3 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]

4 4 Big Picture Safe Programming Languages and Type systems Static Program Analysis Dynamic Program Analysis Model Based Software Development and Analysis Model Checking and Theorem Proving Runtime Monitoring Testing

5 5 Big Picture Safe Programming Languages and Type systems Model Checking and Theorem Proving Runtime Monitoring Model Based Software Development and Analysis Static Program Analysis Dynamic Program Analysis Testing

6 6 A Familiar Program: QuickSort void 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); }

7 7 A Familiar Program: QuickSort void 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?

8 8 Automated Test Generation Studied since 70’s  King 76, Myers 79 30 years have passed, and yet no effective solution What Happened?

9 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

10 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

11 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?

12 12 Systematic Automated Testing Testing Static Analysis Automated Theorem Proving Model-Checking and Formal Methods Constraint Solving

13 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

14 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

15 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

16 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 00 0 0 1 1 1 1 1 1

17 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 NY NY ERROR

18 18 Concolic Testing: Finding Security and Safety Bugs Divide by 0 Error x = 3 / i; Buffer Overflow a[i] = 4;

19 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

20 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/2 32

21 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

22 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

23 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; }

24 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 = 7x = x 0, y = y 0

25 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 = x 0, y = y 0, z = 2*y 0

26 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 = x 0, y = y 0, z = 2*y 0 2*y 0 != x 0

27 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*y 0 != x 0 Solve: 2*y 0 == x 0 Solution: x 0 = 2, y 0 = 1 x = 22, y = 7, z = 14 x = x 0, y = y 0, z = 2*y 0

28 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 = 1x = x 0, y = y 0

29 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 = x 0, y = y 0, z = 2*y 0

30 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 = x 0, y = y 0, z = 2*y 0 2*y 0 == x 0

31 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 = x 0, y = y 0, z = 2*y 0 2*y 0 == x 0 x 0 <= y 0 +10

32 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 = x 0, y = y 0, z = 2*y 0 Solve: (2*y 0 == x 0 ) and (x 0 > y 0 + 10) Solution: x 0 = 30, y 0 = 15 2*y 0 == x 0 x 0 <= y 0 +10

33 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 = 15x = x 0, y = y 0

34 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 = 15x = x 0, y = y 0 2*y 0 == x 0 x 0 > y 0 +10 Program Error

35 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 FF F F T T T T T T

36 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 FF F F T T T T T T

37 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 FF F F T T T T T T

38 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 FF F F T T T T T T

39 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 FF F F T T T T T T

40 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 FF F F T T T T T T

41 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 = 7x = x 0, y = y 0

42 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 = x 0, y = y 0, z = (y 0 *y 0 )%50 (y 0 *y 0 )%50 !=x 0 Solve: (y 0 *y 0 )%50 == x 0 Don’t know how to solve! Stuck? Novelty: Simultaneous Concrete and Symbolic Execution

43 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 = x 0, y = y 0, z = foo (y 0 ) foo (y 0 ) !=x 0 Novelty: Simultaneous Concrete and Symbolic Execution Solve: foo (y 0 ) == x 0 Don’t know how to solve! Stuck?

44 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 = x 0, y = y 0, z = (y 0 *y 0 )%50 (y 0 *y 0 )%50 !=x 0 Solve: (y 0 *y 0 )%50 == x 0 Don’t know how to solve! Not Stuck! Use concrete state Replace y 0 by 7 (sound) Novelty: Simultaneous Concrete and Symbolic Execution

45 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 = x 0, y = y 0, z = 49 49 !=x 0 Solve: 49 == x 0 Solution : x 0 = 49, y 0 = 7 Novelty: Simultaneous Concrete and Symbolic Execution

46 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 = 7x = x 0, y = y 0 Novelty: Simultaneous Concrete and Symbolic Execution

47 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 = x 0, y = y 0, z = 49 2*y 0 == x 0 x 0 > y 0 +10 Program Error Novelty: Simultaneous Concrete and Symbolic Execution

48 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

49 49 Implementations DART and CUTE for C programs jCUTE for Java programs  Goto http://srl.cs.berkeley.edu/~ksen/ for CUTE and jCUTE binarieshttp://srl.cs.berkeley.edu/~ksen/ 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

50 50 Another Example: Testing Data Structures typedef 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

51 51 CUTE Approach typedef 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=p 0, x=x 0 p, x=236 NULL

52 52 CUTE Approach typedef 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=p 0, x=x 0 p, x=236 NULL

53 53 CUTE Approach typedef 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 x 0 >0 p=p 0, x=x 0 p, x=236 NULL

54 54 CUTE Approach typedef 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 x 0 >0 p=p 0, x=x 0 p, x=236 NULL !(p 0 !=NULL )

55 55 CUTE Approach typedef 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 x 0 >0 p=p 0, x=x 0 p, x=236 NULL p 0 =NULL solve: x 0 >0 and p 0  NULL

56 56 CUTE Approach typedef 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 x 0 >0 p=p 0, x=x 0 p, x=236 NULL p 0 =NULL solve: x 0 >0 and p 0  NULL x 0 =236, p 0 634 NULL

57 57 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634

58 58 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634 x 0 >0

59 59 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634 x 0 >0 p 0  NULL

60 60 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634 x 0 >0 p 0  NULL 2x 0 +1  v 0

61 61 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634 x 0 >0 p 0  NULL 2x 0 +1  v 0

62 62 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634 x 0 >0 p 0  NULL 2x 0 +1  v 0 solve: x 0 >0 and p 0  NULL and 2x 0 +1=v 0

63 63 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=236 NULL 634 x 0 >0 p 0  NULL 2x 0 +1  v 0 solve: x 0 >0 and p 0  NULL and 2x 0 +1=v 0 x 0 =1, p 0 3 NULL

64 64 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3

65 65 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0

66 66 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0 p 0  NULL

67 67 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0 p 0  NULL 2x 0 +1=v 0

68 68 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0 p 0  NULL 2x 0 +1=v 0 n0p0n0p0

69 69 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0 p 0  NULL 2x 0 +1=v 0 n0p0n0p0

70 70 CUTE Approach typedef 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: x 0 >0 and p 0  NULL and 2x 0 +1=v 0 and n 0 =p 0 p=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0 p 0  NULL 2x 0 +1=v 0 n0p0n0p0

71 71 CUTE Approach typedef 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: x 0 >0 and p 0  NULL and 2x 0 +1=v 0 and n 0 =p 0 x 0 =1, p 0 3 p=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 NULL 3 x 0 >0 p 0  NULL 2x 0 +1=v 0 n0p0n0p0

72 72 CUTE Approach typedef 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=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 3

73 73 CUTE Approach typedef 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 x 0 >0 p=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 3

74 74 CUTE Approach typedef 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 x 0 >0 p 0  NULL p=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 3

75 75 CUTE Approach typedef 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 x 0 >0 p 0  NULL 2x 0 +1=v 0 p=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 3

76 76 CUTE Approach typedef 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 x 0 >0 p 0  NULL 2x 0 +1=v 0 n0=p0n0=p0 p=p 0, x=x 0, p->v=v 0, p->next=n 0 p, x=1 3 Program Error

77 77 Pointer Inputs: Input Graph typedef 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; }

78 78 CUTE in a Nutshell Generate concrete inputs one by one  each input leads program along a different path

79 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

80 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

81 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

82 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

83 83 Instrumentation

84 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 C 1, C 2,…, C k such that C i s are symbolic constraints over symbolic input variables C 1 ∧ C 2 ∧ … ∧ C k holds over the path currently executed by the program

85 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 C 1, C 2,…, C k such that C i s are symbolic constraints over symbolic input variables C 1 ∧ C 2 ∧ … ∧ C k holds over the path currently executed by the program Arithmetic Expressions a 1 x 1 +…+a n x n +c Pointer Expressions x

86 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 C 1, C 2,…, C k such that C i s are symbolic constraints over symbolic input variables C 1 ∧ C 2 ∧ … ∧ C k holds over the path currently executed by the program Arithmetic Expressions a 1 x 1 +…+a n x n +c Pointer Expressions x Arithmetic Constraints a 1 x 1 +…+a n x n ¸ 0 Pointer Constraints x=y x  y x=NULL x  NULL

87 87 Constraint Solving Path constraint C 1 ∧ C 2 ∧ … ∧ C k ∧ … ∧ C n

88 88 Constraint Solving Path constraint C 1 ∧ C 2 ∧ … ∧ C k ∧ … ∧ C n Solve C 1 ∧ C 2 ∧ … ∧ !C k to generate next input

89 89 Constraint Solving Path constraint C 1 ∧ C 2 ∧ … ∧ C k ∧ … ∧ C n Solve C 1 ∧ C 2 ∧ … ∧ !C k to generate next input If C k is pointer constraint then invoke pointer solver C i1 ∧ C i2 ∧ … ∧ !C k where ij in [1..k-1] and C ij is pointer constraint If C k is arithmetic constraint then invoke linear solver C i1 ∧ C i2 ∧ … ∧ !C k where ij in [1..k-1] and C ij is arithmetic constraint

90 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)

91 91 SGLIB: popular library for C data-structures Name Run time in seconds # of Iterations # of Branches Explored % Branch Coverage # of Functions Tested # of Bugs Found Array Quick Sort 27324397.7320 Array Heap Sort 4176436100.0020 Linked List257010096.15120 Sorted List2102011096.49110 Doubly Linked List 3131722499.12171 Hash Table11934685.1981 Red Black Tree 26291,000,00024271.18170

92 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

93 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


Download ppt "Dynamic Symbolic Execution CS 8803 FPL Oct 31, 2012 (Slides adapted from Koushik Sen) 1."

Similar presentations


Ads by Google