Modular Procedure Equivalence with Dynamic Heap Allocation
Tim Wood 1 Shuvendu Lahiri 2 Sophia Drossopoulou 1
1Imperial College London 2Microsoft Research
Modular Procedure Equivalence with Dynamic Heap Allocation Tim Wood - - PowerPoint PPT Presentation
Modular Procedure Equivalence with Dynamic Heap Allocation Tim Wood 1 Shuvendu Lahiri 2 Sophia Drossopoulou 1 1 Imperial College London 2 Microsoft Research 12th April 2016 Introduction Subject Procedure equivalence for procedures that
1Imperial College London 2Microsoft Research
◮ How to relate the procedures. ◮ Some sound underapproximations that help.
◮ unbounded dynamic heap memory allocation ◮ unbounded recursion (but not loops) ◮ memory-safe / no pointer arithmetic ◮ sequential
◮ unbounded dynamic heap memory allocation ◮ unbounded recursion (but not loops) ◮ memory-safe / no pointer arithmetic ◮ sequential
◮ differences in allocation order ◮ differences in garbage ◮ equivalent rearrangements of existing memory
◮ unbounded dynamic heap memory allocation ◮ unbounded recursion (but not loops) ◮ memory-safe / no pointer arithmetic ◮ sequential
◮ differences in allocation order ◮ differences in garbage ◮ equivalent rearrangements of existing memory
◮ take advantage of procedure contracts if present
def
C φ3 ∧ φ2, s2 C φ4
1Lahiri, Shuvendu K., et al. ”Symdiff: A language-agnostic semantic diff
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
r.v := new;
20 21
rl := new;
22
copy ’(t.l, rl);
23
r.v.l := rl.v;
24 25
rr := new;
26
copy ’(t.r, rr);
27
r.v.r := rr.v;
28 }
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
r.v := new;
20 21
rl := new;
22
copy ’(t.l, rl);
23
r.v.l := rl.v;
24 25
rr := new;
26
copy ’(t.r, rr);
27
r.v.r := rr.v;
28 }
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
rl := new;
20
copy ’(t.l, rl);
21 22
rr := new;
23
copy ’(t.r, rr);
24 25
r.v := new;
26
r.v.l := rl.v;
27
r.v.r := rr.v;
28 }
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
rl := new;
20
copy ’(t.l, rl);
21 22
rr := new;
23
copy ’(t.r, rr);
24 25
r.v := new;
26
r.v.l := rl.v;
27
r.v.r := rr.v;
28 }
def
C φ3 ∧ φ2, s2 C φ4
◮ ∃ a bijection between the allocated addresses in φ1 and φ2 ◮ the bijection preserves the shape of the stores
1 copy(t,r) 2 { 3
n := new;
4 5
if(t = null) return;
6 7
rl := new;
8
copy(t.l, rl);
9
n.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
n.r := rr.v;
14 15
r.v := n;
16 } 17 copy ’(t,r) 18 { 19 20 21
if(t = null) return;
22 23
rl := new;
24
copy ’(t.l, rl);
25 26
rr := new;
27
copy ’(t.r, rr);
28 29
r.v := new;
30
r.v.l := rl.v;
31
r.v.r := rr.v;
32 }
◮ ∃ a bijection between the reachable addresses in φ1 and φ2 ◮ the bijection preserves the shape of the reachable parts of the
1 copy(t,r) 2 { 3
n := new;
4 5
if(t = null) return;
6 7
rl := new;
8
copy(t.l, rl);
9
n.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
n.r := rr.v;
14 15
r.v := n;
16 } 17 copy ’(t,r) 18 { 19 20 21
if(t = null) return;
22 23
rl := new;
24
copy ’(t.l, rl);
25 26
rr := new;
27
copy ’(t.r, rr);
28 29
r.v := new;
30
r.v.l := rl.v;
31
r.v.r := rr.v;
32 }
◮ The garbage is still reachable at the end of the procedure ◮ The stack variables differ
def
C φ3 ∧ φ2, s2 C φ4
1 noop(x) 2
requires ...
3 { 4 5 6 7 } 8 swap(x) 9
requires ...
10 { 11
t := x.f;
12
x.f := x.g;
13
x.g := t;
14 }
◮ e.g. requires x.f==x.g
def
C φ3 ∧ φ2, s2 C φ4
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
rl := new;
20
copy ’(t.l, rl);
21 22
rr := new;
23
copy ’(t.r, rr);
24 25
r.v := new;
26
r.v.l := rl.v;
27
r.v.r := rr.v;
28 }
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
rl := new;
20
copy ’(t.l, rl);
21 22
rr := new;
23
copy ’(t.r, rr);
24 25
r.v := new;
26
r.v.l := rl.v;
27
r.v.r := rr.v;
28 }
◮ one more object is reachable in copy
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
rl := new;
20
copy ’(t.l, rl);
21 22
rr := new;
23
copy ’(t.r, rr);
24 25
r.v := new;
26
r.v.l := rl.v;
27
r.v.r := rr.v;
28 }
1 copy(t,r) 2 { 3
if(t = null) return;
4 5
r.v := new;
6 7
rl := new;
8
copy(t.l, rl);
9
r.v.l := rl.v;
10 11
rr := new;
12
copy(t.r, rr);
13
r.v.r := rr.v;
14 } 15 copy ’(t,r) 16 { 17
if(t = null) return;
18 19
rl := new;
20
copy ’(t.l, rl);
21 22
rr := new;
23
copy ’(t.r, rr);
24 25
r.v := new;
26
r.v.l := rl.v;
27
r.v.r := rr.v;
28 }
1 F_G ’(t,r) 2
requires ...
3 { 4
heap_start := heap; // save initial heap
5
inline F;
6
heap_F := heap; // save final heap
7 8
havoc heap; // non - deterministic value
9
assume iso(ctx ,t,r,heap_start ,heap); // restore an iso heap
10
inline G;
11
heap_G := heap; // save final heap
12 13
assert iso(ctx ,heap_F ,heap_G); // not heap_F == heap_G
14 }
◮ ∃ a bijection between the reachable addresses in φ1 and φ2 ◮ the bijection preserves the shape of the reachable parts of the
◮ we prove the approximations are sound by hand (meta-proof)
C φ3 ∧ φ2, s C φ4 ∧ φ1 ≈ φ2
C φ3 ∧ φ2, s2 C φ4 =
C φ3 ∧ φ2, s2 C φ4 =
1 F_G ’(t,r) 2
requires ...
3 { 4
heap_start := heap; // save initial heap
5
inline F;
6
heap_F := heap; // save final heap
7 8
havoc heap; // non - deterministic value
9
assume iso(ctx ,t,r,heap_start ,heap); // restore an iso heap
10
assume heap = heap_start ; // restore the equal iso heap
11
inline G;
12
heap_G := heap; // save final heap
13 14
assert iso(ctx ,heap_F ,heap_G); // not heap_F == heap_G
15 }
◮ if we know there must be a bijection ◮ we assume it to be the identity
◮ if we know there must be a bijection ◮ we assume it to be the identity
◮ if we know there must be a bijection ◮ we assume it to be the identity
◮ we know that the effects are isomorphic, but ◮ we don’t know what the bijection between the effects is
◮ we know that the effects are isomorphic, but ◮ we don’t know what the bijection between the effects is
◮ we know that the effects are isomorphic, but ◮ we don’t know what the bijection between the effects is
◮ we know that the effects are isomorphic, but ◮ we don’t know what the bijection between the effects is
◮ equivalent procedures may allocate different garbage
1 copy(t,r) { 2
n := new;
3 4
if(t = null) return;
5 6
rl := new;
7
copy(t.l, rl);
8
n.l := rl.v;
9 10
rr := new;
11
copy(t.r, rr);
12
n.r := rr.v;
13 14
r.v := n;
15 } 16 copy ’(t,r) { 17 18 19
if(t = null) return;
20 21
rl := new;
22
copy ’(t.l, rl);
23 24
rr := new;
25
copy ’(t.r, rr);
26 27
r.v := new;
28
r.v.l := rl.v;
29
r.v.r := rr.v;
30 } 31 copy ’’(t,r) { 32 33 34
if(t = null) return;
35 36
rl := new;
37
copy(t.l, rl);
38 39
rr := new;
40
copy(t.r, rr);
41 42
r.v := new;
43
r.v.l := rl.v;
44
r.v.r := rr.v;
45 }
1 copy(t,r) { 2
n := new;
3 4
if(t = null) return;
5 6
rl := new;
7
copy(t.l, rl);
8
n.l := rl.v;
9 10
rr := new;
11
copy(t.r, rr);
12
n.r := rr.v;
13 14
r.v := n;
15 } 16 copy ’(t,r) { 17 18 19
if(t = null) return;
20 21
rl := new;
22
copy ’(t.l, rl);
23 24
rr := new;
25
copy ’(t.r, rr);
26 27
r.v := new;
28
r.v.l := rl.v;
29
r.v.r := rr.v;
30 } 31 copy ’’(t,r) { 32 33 34
if(t = null) return;
35 36
rl := new;
37
copy(t.l, rl);
38 39
rr := new;
40
copy(t.r, rr);
41 42
r.v := new;
43
r.v.l := rl.v;
44
r.v.r := rr.v;
45 }
1 copy(t,r) { 2
n := new;
3 4
if(t = null) return;
5 6
rl := new;
7
copy(t.l, rl);
8
n.l := rl.v;
9 10
rr := new;
11
copy(t.r, rr);
12
n.r := rr.v;
13 14
r.v := n;
15 } 16 copy ’(t,r) { 17 18 19
if(t = null) return;
20 21
rl := new;
22
copy ’(t.l, rl);
23 24
rr := new;
25
copy ’(t.r, rr);
26 27
r.v := new;
28
r.v.l := rl.v;
29
r.v.r := rr.v;
30 } 31 copy ’’(t,r) { 32 33 34
if(t = null) return;
35 36
rl := new;
37
copy(t.l, rl);
38 39
rr := new;
40
copy(t.r, rr);
41 42
r.v := new;
43
r.v.l := rl.v;
44
r.v.r := rr.v;
45 }
1 copy(t,r) { 2
n := new;
3 4
if(t = null) return;
5 6
rl := new;
7
copy(t.l, rl);
8
n.l := rl.v;
9 10
rr := new;
11
copy(t.r, rr);
12
n.r := rr.v;
13 14
r.v := n;
15 } 16 copy ’(t,r) { 17 18 19
if(t = null) return;
20 21
rr := new;
22
copy(t.r, rr);
23 24
rl := new;
25
copy(t.l, rl);
26 27
r.v := new;
28
r.v.l := rl.v;
29
r.v.r := rr.v;
30 }
1
function Copy_succ(h1:Heap ,t:Ref ,r:Ref ,h2:Heap):bool;
2
function Copy ’_succ(h1:Heap ,t:Ref ,r:Ref ,h2:Heap):bool;
3
procedure Copy(r:Ref ,t:Ref)
4
...
5
free ensures Copy_succ(old(h),r,t,h);
6
axiom (∀h1 ,h2 ,h3 ,h4:Heap , t_1 ,r_1 ,t_2 ,r_2:Ref :: ...
7
Copy_succ(h1 ,t_1 ,r_1 ,h3)
8
∧ Copy ’_succ(h2 ,t_2 ,r_2 ,h4)
9
∧ Call#Iso(h1 ,t_1 ,r_1 ,h2 ,t_2 ,r_2)
10 =
⇒
11
Heap#SameDiff(h1 ,h3 ,h2 ,h4)
12
∧ ...
13
∧ Call#Eq(h1 ,t_1 ,r_1 ,h2 ,t_2 ,r_2));
2Hawblitzel, Chris, et al. ”Towards modularly comparing programs using
1
CopyList(x,r) {
2
if(x!=null ∧ r!=null){
3 4 5 6
rn := new ();
7
CopyList(x.n, rn);
8 9
t := new ();
10
t.n := rn.v;
11
r.v := t;
12 13
}
14 } 15 CopyList ’(x,r) { 16
if(x!=null) {
17
t := new ();
18 19
if(y!=null) {
20
rn := new ();
21
CopyList ’(x.n, rn);
22 23 24
r.v := t;
25
t.n := rn.v;
26
}
27
}
28 }
◮ want to design triggers that instantiate quantifiers more
◮ invent more axioms about which sequences of operations