48
The Yogi Project Software property checking via verification and testing Aditya V. Nori, Sriram K. Rajamani Programming Languages and Tools Microsoft Research India

The Yogi Project Software property checking via verification and testing

  • Upload
    hide

  • View
    25

  • Download
    0

Embed Size (px)

DESCRIPTION

The Yogi Project Software property checking via verification and testing. Aditya V. Nori, Sriram K. Rajamani Programming Languages and Tools Microsoft Research India. The plan. Part 1: Our philosophy, the basic algorithm, related work coffee break - PowerPoint PPT Presentation

Citation preview

Page 1: The Yogi Project Software property checking via verification and testing

The Yogi ProjectSoftware property checking via verification and testing

Aditya V. Nori, Sriram K. Rajamani

Programming Languages and ToolsMicrosoft Research India

Page 2: The Yogi Project Software property checking via verification and testing

The plan

Part 1: Our philosophy, the basic algorithm, related work coffee break

Part 2: Handling programs with pointers and procedures coffee break

Part 3: The engineering

Download: http://research.microsoft.com/yogi

Page 3: The Yogi Project Software property checking via verification and testing

Thanks to our collaborators

Aws Albarghouthi Nels Beckman Patrice Godefroid Bhargav Gulavani Tom Henzinger

Yamini Kannan Rahul Kumar Robert Simmons Sai Deep Tetali Aditya Thakur

Page 4: The Yogi Project Software property checking via verification and testing

Property checking

void f(int *p, int *q){0: *p = 4;1: *q = 5;2: if ()3: error();}

QuestionIs the error unreachable for all possible inputs?

More generally, we are interested in the query

Page 5: The Yogi Project Software property checking via verification and testing
Page 6: The Yogi Project Software property checking via verification and testing

Possible solution: Testing

The “old-fashioned” and practical method of validating software

Generate test inputs and see if we can find a test that violates the assertion

Page 7: The Yogi Project Software property checking via verification and testing

What’s wrong with testing?

If we view testing as a “black-box” activity, Dijkstra is right!After executing many tests, we still don’t know if there is another test that can violatethe assertion

Page 8: The Yogi Project Software property checking via verification and testing

If we view testing as a “white-box” activity, and “observe” what happens inside the program, we can do interesting things:A. For a buggy program, we can generate

tests in a directed manner to find the bug

B. For a correct program, we can prove that the assertion holds for all inputs!

Our hypothesis

Page 9: The Yogi Project Software property checking via verification and testing

Tests: presence of bugs

void foo(int a){0: i = 0;1: c = 0;2: while (i < 1000) {3: c = c + i;4: i = i + 1;

}5: if (a <= 0)6: error();}

𝑎↦−5

Page 10: The Yogi Project Software property checking via verification and testing

Tests: presence of bugs𝑎↦−5

void foo(int a){0: i = 0;1: c = 0;2: while (i < 1000) {3: c = c + i;4: i = i + 1;

}5: if (a <= 0)6: error();}

Page 11: The Yogi Project Software property checking via verification and testing

Proofs: absence of bugs

void foo(int y1, int y2){0: state = 1;1: if (y1) {2: x0 = x0 + 1;

} else {3: x0 = x0 – 1;

}4: if (y2) {5: x1 = x1 + 1;

} else {6: x1 = x1 – 1;

}7: if(state != 1)8: error();}

Exponential number of tests required!

Page 12: The Yogi Project Software property checking via verification and testing

assume(y1)

Proofs: absence of bugs

0

1state = 1∃𝒔1

∃𝒔 2

2

overapproximation

void foo(int y1, int y2){0: state = 1;1: if (y1) {2: x0 = x0 + 1;

} else {3: x0 = x0 – 1;

}4: if (y2) {5: x1 = x1 + 1;

} else {6: x1 = x1 – 1;

}7: if(state != 1)8: error();}

Page 13: The Yogi Project Software property checking via verification and testing

Proofs: absence of bugs

0

1

2

8:error

3

4

5 6

7

state = 1assume(y1) assume(!y1)x0=x0+1 x0=x0-1assume(y2) assume(!y2)

x1=x1+1 x1=x1-1assume(state != 1)9

assume(state == 1)

void foo(int y1, int y2){0: state = 1;1: if (y1) {2: x0 = x0 + 1;

} else {3: x0 = x0 – 1;

}4: if (y2) {5: x1 = x1 + 1;

} else {6: x1 = x1 – 1;

}7: if(state != 1)8: error();}

Page 14: The Yogi Project Software property checking via verification and testing

Proofs: absence of bugs

linear proof exists!

0:state=11:state=1

2:state=1

8:error

3:state=14:state=1

5:state=1 6:state=17:state=1

void foo(int y1, int y2){0: state = 1;1: if (y1) {2: x0 = x0 + 1;

} else {3: x0 = x0 – 1;

}4: if (y2) {5: x1 = x1 + 1;

} else {6: x1 = x1 – 1;

}7: if(state != 1)8: error();}

Page 15: The Yogi Project Software property checking via verification and testing

Algorithm uses only test case generation operations

Maintains two data structures:▪ A forest of reachable concrete states (tests)▪ Under-approximates executions of the program

Yogi: Proofs from Tests

… … …

Page 16: The Yogi Project Software property checking via verification and testing

Algorithm uses only test case generation operations

Maintains two data structures:▪ A forest of reachable concrete states (tests)▪ Under-approximates executions of the program

▪ A region graph (an abstraction)▪ Over-approximates all executions of the program

Yogi: Proofs from Tests

0

1

2

8:error

3

4

5 6

7

state = 1

assume(y1) assume(!y1)

x0=x0+1 x0=x0-1

assume(y2) assume(!y2)

x1=x1+1 x1=x1-1

assume(state != 1)9

assume(state == 1)

Page 17: The Yogi Project Software property checking via verification and testing

Algorithm uses only test case generation operations Maintains two data structures:▪ A forest of reachable concrete states (tests)▪ Under-approximates executions of the program

▪ A region graph (an abstraction)▪ Over-approximates all executions of the program

Our goal: bug finding and proving▪ If a test reaches an error, we have found bug▪ If we refine the abstraction so that there is *no* path from

the initial region to error region, we have a proof Key ideas▪ Frontier: Boundary between tested regions and untested

regions▪ uses only aliases α that are present along concrete tests

that are executed

Yogi: Proofs from Tests

Page 18: The Yogi Project Software property checking via verification and testing

Key ideas

Frontier = edge that crosses from tested to untested region

Step 1: Try to generate a test that crosses the frontier

Perform symbolic simulation on the path until the frontier and generate a constraint

Conjoin with the condition

needed to cross frontier Is satisfiable?

frontier

0

1

2

3

4

7

8

9

5

6

10

𝜑2

𝜑1

Page 19: The Yogi Project Software property checking via verification and testing

Key ideas

Step 1: Try to generate a test that crosses the frontier

Perform symbolic simulation on the path until the frontier and generate a constraint

Conjoin with the condition needed to cross frontier

Is satisfiable? [YES]

Step 2: run the test and extend the frontier

0

1

2

3

4

7

8

9

5

6

10

frontier

Page 20: The Yogi Project Software property checking via verification and testing

Key ideas

Step 1: Try to generate a test that crosses the frontier

Perform symbolic simulation on the path until the frontier and generate a constraint

Conjoin with the condition needed to cross frontier

Is satisfiable? [NO]

Step 2: use to refine so that the frontier moves back!

frontier

0

1

2

3

4:

7

8

9

5

6

10

4:

Page 21: The Yogi Project Software property checking via verification and testing

The Yogi algorithm

Can extend test beyond frontier?

Refine abstraction

Construct initial abstraction

Construct random tests

Test succeeded?

Bug!

Abstraction succeeded?

τ = error path in abstraction f = frontier of error path

yes

no

yes

no

Proof!

yes

no

Input:Program Property

Page 22: The Yogi Project Software property checking via verification and testing

The Yogi algorithm

Can extend test beyond frontier?

Refine abstraction

Construct initial abstraction

Construct random tests

Test succeeded?

Bug!

Abstraction succeeded?

τ = error path in abstraction f = frontier of error path

yes

no

yes

no

Proof!

yes

no

Input:Program Property

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

0 1234

56

789

×

10

𝑦=1

)fronti

er

Symbolic execution +

Theorem proving

Page 23: The Yogi Project Software property checking via verification and testing

Symbolic execution

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

ylockxsymbol table

𝜏=(0,1,2,3,4,7,8,9)

Page 24: The Yogi Project Software property checking via verification and testing

Symbolic execution

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

ylockxsymbol table

𝜏=(0,1,2,3,4,7,8,9)

Page 25: The Yogi Project Software property checking via verification and testing

Symbolic execution

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

ylockxsymbol table

𝜏=(0,1,2,3,4,7,8,9)

Page 26: The Yogi Project Software property checking via verification and testing

Symbolic execution

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

ylockxsymbol table

𝜏=(0,1,2,3,4,7,8,9)

Page 27: The Yogi Project Software property checking via verification and testing

Symbolic execution

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

ylockxsymbol table

𝜏=(0,1,2,3,4,7,8,9)

constraints

Page 28: The Yogi Project Software property checking via verification and testing

Symbolic execution

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

ylockxsymbol table

𝜏=(0,1,2,3,4,7,8,9)

constraints

Page 29: The Yogi Project Software property checking via verification and testing

The Yogi algorithm

Can extend test beyond frontier?

Refine abstraction

Construct initial abstraction

Construct random tests

Test succeeded?

Bug!

Abstraction succeeded?

τ = error path in abstraction f = frontier of error path

yes

no

yes

no

Proof!

yes

no

Input:Program Property

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

0 1234

56

789

×

10)

frontier

NO

Page 30: The Yogi Project Software property checking via verification and testing

Refinement

89

8:¬ρ 8:ρ9

refine

𝑊𝑃 ¿

0 1234

56

789

10

assume(lock != 1)

Candidates for refinement predicate

A. Strongest postcondition (SP) along the path up to the frontier

B. Weakest precondition (WP): C. Any formula separating the two above

(e.g., an interpolant)

Page 31: The Yogi Project Software property checking via verification and testing

Refinement0 1

234

56

7

910

8: 8:p

89

8:¬ρ 8:ρ9

refine

𝜌=( 𝑙𝑜𝑐𝑘 !=1)

assume(lock != 1)

Page 32: The Yogi Project Software property checking via verification and testing

Next iteration

Can extend test beyond frontier?

Refine abstraction

Construct initial abstraction

Construct random tests

Test succeeded?

Bug!

Abstractionsucceeded?

τ = error path in abstraction f = frontier of error path

yes

no

yes

no

Proof! yes

no

Input:Program Property

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

×

0 1234

56

7

910

8: 8:p

𝜏=(0,1,2,3,4,7 ,<8 ,𝑝>,9) frontier

Page 33: The Yogi Project Software property checking via verification and testing

Correct, the program is …

void f(int y){0: int lock=0, x;1: do {2: lock = 1;3: x = y;4: if (*) {5: lock = 0;6: y = y+1; }7: } while (x != y)8: if (lock != 1)9: error();10:}

01

2

34⋀¬s5⋀¬s6⋀¬r

9

7⋀¬q8⋀¬p

4⋀s5⋀s6⋀r7⋀q

8⋀p

10

Page 34: The Yogi Project Software property checking via verification and testing

Soundness and Termination

• Theorem: Suppose we run Yogi on any program and property – If Yogi returns , then the abstract

program simulates , and thus is a proof that does not reach

– If Yogi returns , then is an error trace

Page 35: The Yogi Project Software property checking via verification and testing

35

prog. P’prog. P

SLIC rule

SLAM and CEGAR

boolean program

pathpredicates

slic

c2bp

bebop

newton

Page 36: The Yogi Project Software property checking via verification and testing

CEGAR (SLAM) on an example

void foo(int a){0: i = 0;1: c = 0;2: while (i < 1000) {3: c = c + i;4: i = i + 1; }5: if(a <= 0)6: error();}

CEGAR will need to refine the loop 1000 times!

01

2

3

456

Page 37: The Yogi Project Software property checking via verification and testing

Yogi on the same example

𝜏=(0,1,2 ,(3,4,2)1000 ,5,6)

Symbolic execution produces the constraint

frontier

01

2

3

456

void foo(int a){0: i = 0;1: c = 0;2: while (i < 1000) {3: c = c + i;4: i = i + 1; }5: if(a <= 0)6: error();}

Page 38: The Yogi Project Software property checking via verification and testing

Directed testing with DARTvoid test_me(int x, int y){1: int z = 2*x;2: if (z==y) {3: if (y == x+10)4: error(); }5: return; }

Step 1:• Try random test:

DART: Directed Automated Random Testing

Patrice Godefroid, Nils Klarlund, Koushik Sen, PLDI 2005

Page 39: The Yogi Project Software property checking via verification and testing

Directed testing with DART

Step 1:• Try random test:

Step 2:• Do a “parallel” symbolic

execution• Collect constraints and negate

the last branch • Solve for the constraint: • Try next test

void test_me(int x, int y){1: int z = 2*x;2: if (z==y) {3: if (y == x+10)4: error(); }5: return; }

DART: Directed Automated Random Testing

Patrice Godefroid, Nils Klarlund, Koushik Sen, PLDI 2005

Page 40: The Yogi Project Software property checking via verification and testing

Directed testing with DART

Step 1:• Try test:

Step 2:• Do a “parallel” symbolic

execution• Collect constraints and negate

the last branch • Solve for constraint: • Try next test • Find error!

void test_me(int x, int y){1: int z = 2*x;2: if (z==y) {3: if (y == x+10)4: abort(); }5: return; }

DART: Directed Automated Random Testing

Patrice Godefroid, Nils Klarlund, Koushik Sen, PLDI 2005

Page 41: The Yogi Project Software property checking via verification and testing

Path explosion with DARTvoid foo(int x1, int x2,…, int xn){ lock = 1 if (x1) g = g + 1; else g = g – 1; if (x2) g = g + 1; else g = g – 1; ... if (xn) g = g + 1; else g = g – 1; if (lock != 1) error();}

• Full path coverage expensive

• Yogi uses abstraction to ignore irrelevant states and efficiently computes a proof

Page 42: The Yogi Project Software property checking via verification and testing

Partition refinement, Bisimulations and the Lee-Yannakakis algorithm

Init Error

Page 43: The Yogi Project Software property checking via verification and testing

Bisimulations

• Partition is stable if for every region we have that doesn’t split any other region in a non-trivial manner

• Bisimulation = coarsest stable partition, precise abstraction for property checking

Init Error

Between regions

Between concrete

states

Page 44: The Yogi Project Software property checking via verification and testing

Partition refinement

Partition refinement:• For every region compute

and split every partition as needed

• Until no splits can be generated

𝜎 1 𝜎 2𝜎 3

Page 45: The Yogi Project Software property checking via verification and testing

Partition refinement

• On termination generates a bisimulation:

• For every pair of regions and we have that iff there is some state and some state such that

𝜎 1 𝑝𝑟𝑒 (𝜎 ¿¿3)¿ 𝜎 3On termination Yogi generates a simulation:For every pair of regions and , we have if there is some state and some state such that then

Partition refinement:• For every region compute

and split every partition as needed

• Until no splits can be generated

Page 46: The Yogi Project Software property checking via verification and testing

Lee-Yannakakis 92 andPasaraneu-Palanek-Visser 05

• Compute reachable bisimulation quotient

• Do splitting only for reachable portion of the state space

• Generate witnesses for reachability of regions and split only regions which have reachable witnesses

• On termination reachable partitions are a bisimulation:

• For every pair of regions and we have that iff there is some state and some state such that

ErrorInit

Can split

Can’t split

Page 47: The Yogi Project Software property checking via verification and testing

Comparison with Yogi

void foo(int y){0: while(y>0){1: y = y -1; }2: if(false)3: error();}

0

1

2

• Yogi terminates with this abstraction shown

• In contrast, Lee-Yannakakis will refine regions and with predicates forever

• Reachable bisumulation quotient for this program is infinite

• But bisimulation quotient is not necessary to prove the property. Simulation is sufficient, and Yogi is able to find such a simulation

3

Page 48: The Yogi Project Software property checking via verification and testing

Summary so far …

Yogi combines testing and verification Maintains a partition (region graph) just like SLAM Generates witnesses using test case generation like DART Core operation:▪ If frontier can be extended by a test case, extend▪ If frontier cannot be extended, refine the pre-state region

Combines advantages of SLAM and DART Uses regions to avoid exploring exponential number of paths (like

SLAM) Uses tests to explore complicated code (like DART)

Computes simulations that can do proofs

Terminates in more cases than algorithms that compute bisimulation quotients, such as Lee-Yannakakis