Galois Transformers and Modular Abstract Interpreters
Reusable Metatheory for Program Analysis
David Darais
University of Maryland
Matthew Might
University of Utah
David Van Horn
University of Maryland
Galois Transformers and Modular Abstract Interpreters Reusable - - PowerPoint PPT Presentation
Galois Transformers and Modular Abstract Interpreters Reusable Metatheory for Program Analysis David Darais Matthew Might David Van Horn University of Maryland University of Utah University of Maryland Program Analysis 2 Program
Galois Transformers and Modular Abstract Interpreters
Reusable Metatheory for Program Analysis
David Darais
University of Maryland
Matthew Might
University of Utah
David Van Horn
University of Maryland
2
2
2
2
Reusable components for building program analyzers
2
Reusable components for building program analyzers
Variations in path/flow sensitivity of your analyzer for free
2
3
(in the paradigm of abstract interpretation)
4
Program
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program
5
Analysis Property
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program
x/0
Analysis Property
6
Abstract Values
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program
x/0
Analysis Property
ℤ ⊑ {-,0,+}
Abstract Values
Implement
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
7
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program
x/0
Analysis Property
ℤ ⊑ {-,0,+}
Abstract Values
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
Implement
Get Results
N ∈ {-,0,+} x ∈ {0,+} y ∈ {-,0,+} UNSAFE: {100/N} UNSAFE: {100/x}
8
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program
x/0
Analysis Property
ℤ ⊑ {-,0,+}
Abstract Values
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
Implement
N ∈ {-,0,+} x ∈ {0,+} y ∈ {-,0,+} UNSAFE: {100/N} UNSAFE: {100/x}
Get Results
⟦e⟧ ∈ ⟦analyze(e)⟧
9
Prove Correct
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
10
Get Results
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}} N ∈ {-,0,+} x ∈ {0,+} y ∈ {-,0,+} UNSAFE: {100/N} UNSAFE: {100/x}
⟦e⟧ ∈ ⟦analyze(e)⟧
Prove Correct
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
11
Flow-insensitive
results : var ↦ !({-,0,+})
N ∈ {-,0,+} x ∈ {0,+} y ∈ {-,0,+} UNSAFE: {100/N} UNSAFE: {100/x}
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
12
Flow-sensitive
results : loc ↦ (var ↦ !({-,0,+}))
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
4: x ∈ {0,+} 4.T: N ∈ {-,+} 5.F: x ∈ {0,+} N,y ∈ {-,0,+} UNSAFE: {100/x}
13
Flow-sensitive
results : loc ↦ (var ↦ !({-,0,+}))
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
14
Path-sensitive
results : loc ↦ !(var ↦ !({-,0,+}))
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
15
Path-sensitive
results : loc ↦ !(var ↦ !({-,0,+}))
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
16
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
16
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
17
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ .. 4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
safe_fun.js
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
17
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
safe_fun.js
redesigning from scratch?
different languages?
designs?
18
19
Monad Transformers Galois Connections Galoiѕ Transformerѕ Compositional interpreters Compositional abstractions Compositional abstract interpreters
+ =
20
21
(bind)
some effects, then returns t"
22
type "(t)
N ∈ {-,0,+} x ∈ {0,+} y ∈ {-,0,+} UNSAFE: {100/N} UNSAFE: {100/x}
Analysis Property
x/0
Abstract Domain
ℤ ⊑ {-,0,+}
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
Program Implement
23
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ .. 0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
24
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
24
value := ℤ ∪ # ρ ∈ env := var ↦ value ⟦_⟧ : atom → "(value) chooseBool : value♯ → "♯(#)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
24
value := ℤ ∪ # ρ ∈ env := var ↦ value ⟦_⟧ : atom → "(value) chooseBool : value♯ → "♯(#) type "(t)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
24
value := ℤ ∪ # ρ ∈ env := var ↦ value ⟦_⟧ : atom → "(value) chooseBool : value♯ → "♯(#) type "(t)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
step : exp → "(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯ ρ ← getEnv putEnv(ρ[x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯ case v of True → return(e₁) False → return(e₂) _ → fail
25
value := ℤ ∪ # ρ ∈ env := var ↦ value ⟦_⟧ : atom → "(value) chooseBool : value♯ → "♯(#)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
step : exp → "(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯ ρ ← getEnv putEnv(ρ[x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯ case v of True → return(e₁) False → return(e₂) _ → fail
type "(t)
26
value := ℤ ∪ # ρ ∈ env := var ↦ value ⟦_⟧ : atom → "(value) chooseBool : value♯ → "♯(#)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
step : exp → "(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯ ρ ← getEnv putEnv(ρ[x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯ case v of True → return(e₁) False → return(e₂) _ → fail
type "(t)
27
value := ℤ ∪ # ρ ∈ env := var ↦ value ⟦_⟧ : atom → "(value) chooseBool : value♯ → "♯(#)
step : exp → "(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯ ρ ← getEnv putEnv(ρ[x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯ case v of True → return(e₁) False → return(e₂) _ → fail
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
type "(t)
28
value♯ := !({-,0,+}) ∪ !(#) ρ ∈ env♯ := var ↦ value♯ ⟦_⟧♯ : atom → "♯(value♯) chooseBool : value♯ → "♯(#) type "♯(t)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
step : exp → "♯(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯♯ ρ ← getEnv putEnv(ρ[x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯♯ case v of True → return(e₁) False → return(e₂) _ → fail
29
value♯ := !({-,0,+}) ∪ !(#) ρ ∈ env♯ := var ↦ value♯ ⟦_⟧♯ : atom → "♯(value♯) chooseBool : value♯ → "♯(#)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
step : exp → "♯(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯♯ ρ ← getEnv putEnv(ρ ⊔ [x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯♯ case v of True → return(e₁) False → return(e₂) _ → fail
type "♯(t)
31
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
value♯ := !({-,0,+}) ∪ !(#) ρ ∈ env♯ := var ↦ value♯ ⟦_⟧♯ : atom → "♯(value♯) chooseBool : value♯ → "♯(#) type "♯(t)
step : exp → "♯(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯♯ ρ ← getEnv putEnv(ρ ⊔ [x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯♯ b ← chooseBool(v) case b of True → return(e₁) False → return(e₂) step : exp → "♯(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯♯ ρ ← getEnv putEnv(ρ ⊔ [x↦v])⊔ return(SKIP)
33
type "♯(t)
0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
step : exp → "♯(exp)♯ step(x := æ) := do v ← ⟦æ⟧♯♯ ρ ← getEnv putEnv(ρ ⊔ [x↦v])⊔ return(SKIP) step(IF(æ){e₁}{e₂}):= do v ← ⟦æ⟧♯♯ b ← chooseBool(v) case b of True → return(e₁) False → return(e₂)
value♯ := !({-,0,+}) ∪ !(#) ρ ∈ env♯ := var ↦ value♯ ⟦_⟧♯ : atom → "♯(value♯) chooseBool : value♯ → "♯(#)
Monadic Abs. Interpreters
34
state machine or constraint system
35
36
37
type "(t)
38
type "(t)
state machine or constraint system
39
41
State[$] Nondet
get : "($) put : $ → "(1) fail : ∀ A. "(A) _⊞_ : ∀ A. "(A)×"(A) → "(A)
42
StateT[$] NondetT
44
StateT[$]
effects "
NondetT
StateT[$](") effects + state
45
=
" StateT[$] effects
NondetT
NondetT(") effects + nondet NondetT
46
StateT[$]
effects "
=
type "(t)
47
48
ID
=
type "(t)
49
ID NondetT
=
type "(t)
50
ID StateT[env] NondetT
=
type "(t)
51
ID StateT[env♯] NondetT
=
type "♯(t)
53
ID StateT[env♯] NondetT
=
type "♯(t)
Path-sensitive
NondetT StateT[env♯]
54
ID
=
type "♯(t)
Flow-insensitive
StateT[env♯] NondetT
Flow-insensitive
56
StateT[env♯] NondetT
⊒
Path-sensitive
StateT[env♯] NondetT
Flow-insensitive
StateT[env♯] NondetT
Path-sensitive
57
FlowT[env♯]
⊒ ⊒
Flow-sensitive
!(exp) × env♯ exp ↦ !(env♯)
StateT[env♯] NondetT
Flow-insensitive
StateT[env♯] NondetT
Path-sensitive
58
FlowT[env♯]
⊒ ⊒
Flow-sensitive
exp ↦ env♯
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
!(exp) × env♯ exp ↦ !(env♯)
Flow-insensitive Path-sensitive Flow-sensitive
exp ↦ env♯
59
4: x ∈ {0,+} 4.T: N ∈ {-,+} 5.F: x ∈ {0,+} N,y ∈ {-,0,+} UNSAFE: {100/x} N ∈ {-,0,+} x ∈ {0,+} y ∈ {-,0,+} UNSAFE: {100/N} UNSAFE: {100/x}
and NondetT
60
interpreters—also apply to abstract interpreters!
61
62
type "(t)
63
FlowT[$]
type "(t)
64
FlowT[$]
type "(t)
65
and proof of correctness for abstract interpreter
66
FlowT[env♯] StateT[env♯] NondetT
67
semantics independent library
helpful, but not necessary
68
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
69
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE ? 0: int x y; // global state 1: void safe_fun(int N) { 2: if (N≠0) {x := 0;} 3: else {x := 1;} 4: if (N≠0) {y := 100/N;} 5: else {y := 100/x;}}
Program Analysis Property
x/0
Abstract Values
ℤ ⊑ {-,0,+}
Implement
70
Get Results Prove Correct
⟦e⟧ ∈ ⟦analyze(e)⟧
analyze : exp → results analyze(x := æ) := .. x .. æ .. analyze(IF(æ){e₁}{e₂}) := .. æ .. e₁ .. e₂ ..
4: N∈{-,+},x∈{0} 4: N∈{0},x∈{+} N∈{-,+},y∈{-,0,+} N∈{0},y∈{0,+} SAFE
safe_fun.js
design choices, like context or object sensitivity
interpreters; might relate to pushdown analysis
(w/Van Horn)
71 David Darais — GTs+MAIs — OOPSLA2015