Download presentation
Presentation is loading. Please wait.
Published byMolly Walton Modified over 9 years ago
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 n0p0n0p0
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 n0p0n0p0
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 n0p0n0p0
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 n0p0n0p0
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
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.