Software Verification With Liquid Types
description
Transcript of Software Verification With Liquid Types
Software VerificationWith Liquid Types
Ranjit Jhala, UC San Diego(with Pat Rondon, Ming Kawaguchi)
int min_index(int [] a){ r = 0; n = a.length; for (i = 0; i < n; i++){
if (a[i] < a[r]) r = i; } return r;}
Software Verification
int min_index(int [] a){ r = 0; n = a.length; for (i = 0; i < n; i++){
if (a[i] < a[r]) r = i; } return r;}
Example: Memory Safety
Access In Array Bounds ?
int min_index(int [] a){ r = 0; n = a.length; for (i = 0; i < n; i++){
if (a[i] < a[r]) r = i; } return r;}
Example: Memory Safety
How to prove assert ?
assert (0<=r && r<n)
int min_index(int [] a){ r = 0; n = a.length; //@invariant: 0·iÆ 0·r Æ
r<nfor (i = 0; i < n; i++){
if (a[i] < a[r]) r = i; } return r;}
Example: Memory Safety
int min_index(int [] a){ r = 0; n = a.length; //@invariant: 0·iÆ 0·r Æ
r<nfor (i = 0; i < n; i++){
if (a[i] < a[r]) r = i; } return r;}
Invariants
By Man [Floyd-Hoare]Or Machine [Cousot-Cousot]
But, what of …
ClosuresCollections
PolymorphismData Structures …?
H-O Logics?Quantifiers?
Induction?
But, what of …
Automation
H-O Logics?Quantifiers?
Induction?A Solution: Types
Part I
Part II
Part III
Types and Invariants
Closures, Collections, Generics
Induction for Data Structures
Part ITypes and Invariants
Part IRefinement Types
[Zenger ’97, Owre-Rushby-Shankar ’98, Xi-Pfenning ’98]
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
min_index: a:int array -> int
min_index: {a:int array|0≺ a.len} -> {v:int|0≼ v≺ a.len}
Type
Refinement
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
min_index: {a:int array|0≺ a.len} -> {v:int|0≼ v≺ a.len}
“Requires”
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
min_index: {a:int array|0≺ a.len} -> {v:int|0≼ v≺ a.len}
“Ensures”
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
{r:int|0≼ r≺ a.len}->{i:int|0≼ i}->{v:int|0≼ v≺ a.len}
loop:
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
{r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
loop:
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
{r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}“Requires”
loop:
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
{r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len} “Ensures”
loop:
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Part IRefinement Types
1. Type-checking2. Refine-checking3. Refine-Inference
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop : r:int -> i:int -> int
Assume
1
loop : … r : int i : int
Prove output is int
r “subtype-of” int⊢
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop : r:int -> i:int -> int
Assume
1
loop : … r : int i : int
Prove output is int
r <: int⊢
loop : r:int -> i:int -> int
Assume
12
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0loop : … r : int i : int
Prove index is int⊢ i <: int
loop : r:int -> i:int -> int
Assume
1
3
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
2
loop : … i : int r : int r': int i': int
Prove input is int⊢ r' <: int
loop : r:int -> i:int -> int
Assume
loop r' i' <: int
Prove output is int
1
3 4
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
2
loop : … i : int r : int r': int i': int
⊢
loop : r:int -> i:int -> int
Assume Prove input is int
1
3 4
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
2
5
loop : … i : int r : int r': int i': int
⊢0 <: int
Part IRefinement Types
1. Type-checking2. Refine-checking3. Refine-Inference
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop{r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺
a.len}
min_index{a:0≺ a.len}->{v:0≼ v≺ a.len}
r :0≼ r≺ a.leni :0≼ i
Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
3 4
2
5
[i≽ a.len]
r :0≼ r≺ a.leni :0≼ i [i≽ a.len]
Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured⊢ {v=r} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
r :0≼ r≺ a.leni :0≼ i [i≽ a.len]
Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured⊢ {v=r} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured⊢ {v=r} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
0≼ r≺ a.len⋀ 0≼ i⋀ i≽ a.len
Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured
{v=r} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
0≼ r≺ a.len⋀ 0≼ i⋀ i≽ a.len ⋀
Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
0≼ r≺ a.len⋀ 0≼ i⋀ i≽ a.len ⋀ ⇒v=r 0≼ v≺ a.len
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
v=r 0≼ v≺ a.len⋀ 0≼ r≺ a.len⋀ 0≼ i⋀ i≽ a.lenIs Valid?Yes. ⇒
Assume
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
r :0≼ r≺ a.leni :0≼ i [i≺ a.len]
Prove index in bounds⊢{v=i} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
1
3 4
2
5
[i≺ a.len]
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
⇒v=i 0≼ v≺ a.len
⋀ 0≼ r≺ a.len⋀ 0≼ i⋀ i≺ a.lenIs Valid?Yes.
2
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove input as required⊢{v=r'} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
1
3 4
2
5
Assume
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0r :0≼ r≺ a.leni :0≼ i [i≺ a.len]i':i'=i+1
Prove input as required⊢{v=r'} <: {0≼ v≺ a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
3
r':r'=i ∨ r'=r
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
3
⇒v=r' 0≼ v≺ a.len
⋀Is Valid?Yes!
0≼ r≺ a.len⋀ 0≼ i⋀ i≺ a.len⋀ r'=i ∨ r'=r⋀ i'=i+1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured⊢{0≼ v≺a.len}<: {0≼ v≺a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
1
3 4
2
5
Trivial.
Assume
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
a :0 ≺ a.lenProve input as required⊢ {v=0}<: {0≼ v≺a.len}
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
1
3 4
2
5
⇒⋀
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
v=0 0≼ v≺a.len
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
5
Is Valid?Indeed.
0 ≺ a.len
Part IRefinement Types
1. Type-checking2. Refine-checking3. Refine-Inference
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop{r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺
a.len}
min_index{a:0≺ a.len}->{v:0≼ v≺ a.len}
loop{r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺
a.len}
min_index{a:0≺ a.len}->{v:0≼ v≺ a.len}
Writing Types is Hard Work!
Automatic Refinement Inference
Writing Types is Hard Work!
loop: ???
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
loop: ???
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
loop : r:int -> i:int -> int
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
loop : {r:int|?} -> {i:int|?} ->
{v:int|?}
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
Types + X for unknown refinements
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]Type Checking, but with Templates
r : Xr
i : Xi
[i≽ a.len]Assume
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured
3 4
2
5
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
⊢ {v=r} <:
{Xout}
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
⋀ Xr ⋀ Xi ⋀ i≽ a.lenIs Valid? ⇒ v=r Xout
“It is too soon to say. ”
1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
3 4
2
5
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
Xr ⋀ Xi ⋀ i≽ a.len ⋀ v=r ⇒ XoutConstraint 1
Assume
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove index in bounds
⊢{v=i} <: {0≼ v≺ a.len}
1
3 4
2
5
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
r : Xr
i : Xi
[i≺ a.len]
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
2
Xr ⋀ Xi ⋀i≺ a.len ⋀ v=i ⇒ 0≼ v≺ a.lenConstraint 2
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove input as required⊢{v:v=r'} <: {r:int|Xr}
1
3 4
2
5
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
Assume r :Xr
i :Xi
[i≺ a.len]r':r'=i ∨ r'=ri':i'=i+1
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
loop: {r:0≼ r≺ a.len}->{i:0≼ i}->{v:0≼ v≺ a.len}
Constraint 3
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ v=r' Xr[v/r ]⇒
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove output is ensured
1
3 4
2
5
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
Constraint 4
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout Xout⇒
let min_index a = let rec loop r i = if i >= a.length then r
else let r' = a[i] < a[r] ?
i : r let i' = i + 1 loop r' i' loop 0 0
Prove input as required
1
3 4
2
5
loop :{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
Constraint 5 0≺ a.len ⋀ v=0 Xr[v/r ]⇒
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]Type Checking, but with Templates
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
0≺ a.len ⋀ v=0 ⇒ Xr[v/r ]
1
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ v=r' ⇒ Xr[v/r ]
Xr ⋀ Xi ⋀i≺ a.len ⋀ v=i ⇒ 0≼ v≺ a.lenXr ⋀ Xi ⋀ i≽ a.len ⋀ v=r ⇒ Xout
2345
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout 4
⋀Xi ⋀ P ⇒ X
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout 4
⋀Xi ⋀ P ⇒ X
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout 4
⋀Xi ⋀ P ⇒ X
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
0≺ a.len ⋀ v=0 ⇒ Xr[v/r ]
1
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ v=r' ⇒ Xr[v/r ]
Xr ⋀ Xi ⋀ i≽ a.len ⋀ v=r ⇒ Xout
2345
Xr ⋀ Xi ⋀i≺ a.len ⋀ v=i ⇒ 0≼ v≺ a.len⋀Xi ⋀ P ⇒ Q
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
0≺ a.len ⋀ v=0 ⇒ Xr[v/r ]
1
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ v=r' ⇒ Xr[v/r ]
Xr ⋀ Xi ⋀ i≽ a.len ⋀ v=r ⇒ Xout
2345
Xr ⋀ Xi ⋀i≺ a.len ⋀ v=i ⇒ 0≼ v≺ a.len⋀Xi ⋀ P ⇒ Q
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
0≺ a.len ⋀ v=0 ⇒ Xr[v/r ]
1
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ Xout ⇒ Xout
Xr ⋀ Xi ⋀ i≺ a.len ⋀ (r'=i∨ r'=r) ⋀ v=r' ⇒ Xr[v/r ]
Xr ⋀ Xi ⋀ i≽ a.len ⋀ v=r ⇒ Xout
2345
Xr ⋀ Xi ⋀i≺ a.len ⋀ v=i ⇒ 0≼ v≺ a.len⋀Xi ⋀ P ⇒ Q
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]
⋀Xi ⋀ P ⇒ Q⋀Xi ⋀ P ⇒ X (Post*)
(Safe)
Automatic Refinement Inference
1. Templates2. Constraints 3. Fixpoint [MC/AI]⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
“Horn Clauses”
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
“Positive Implications”
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
“Non-Linear Implications”
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
1. Reduce to simple “first-order” program Reuse any abs-interpreter, model-checker e.g. Interproc, SLAM, BLAST, ARMC [CAV 11]
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
2. Monomial Predicate Abstraction (“Houdini”) Input : Candidates Q = {q1…qn} Output : X ↦ ⋀qj satisfying constraints
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
2. Monomial Predicate Abstraction (“Houdini”) Input : Q = {⋆≼ v, v≼ ⋆, v≺ ⋆, v≺ ⋆.len} ⋆ = any variable or constant
Automatic Refinement Inference
Fixpoint Solution ⋀Xi ⋀ P ⇒ X⋀Xi ⋀ P ⇒ Q
2. Monomial Predicate Abstraction (“Houdini”) Input : Q = {⋆≼ v, v≼ ⋆, v≺ ⋆, v≺ ⋆.len} Output : Xr = 0≼ v ⋀ v≺ a.len
Xi = 0≼ v Xout = 0≼ v ⋀ v≺ a.len
2. Monomial Predicate Abstraction (“Houdini”) Input : Q = {⋆≼ v, v≼ ⋆, v≺ ⋆, v≺ ⋆.len} Output : Xr = 0≼ v ⋀ v≺ a.len
Xi = 0≼ v Xout = 0≼ v ⋀ v≺ a.len
Automatic Refinement Inference
Plug Solution In Template
loop:{r:int|Xr} -> {i:int|Xi} -> {v:int|Xout}
2. Monomial Predicate Abstraction (“Houdini”) Input : Q = {⋆≼ v, v≼ ⋆, v≺ ⋆, v≺ ⋆.len} Output : Xr = 0≼ v ⋀ v≺ a.len
Xi = 0≼ v Xout = 0≼ v ⋀ v≺ a.len
Automatic Refinement Inference
Plug Solution In Template
loop:{r:int|0≼r≺a.len}->{i:int|0≼ i}->{v:int|0≼
v≺a.len}
Automatic Refinement Inference
loop:{r:int|0≼r≺a.len}->{i:int|0≼ i}->{v:int|0≼
v≺a.len}Inferred Refinement Type
Automatic Refinement Inference
loop:{r:int|0≼r≺a.len}->{i:int|0≼ i}->{v:int|0≼
v≺a.len}Liquid Type of loop
RecapProgram
Templates
Constraints
AI, MC, PA …
{i:int|Xi} -> {v:int|Xo}
⋀Xi ⋀ P ⇒ X
Part I
Part II
Part III
Liquid Types
Closures, Collections, Generics
Induction for Data Structures
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Closures / Higher-Order Functions
let rec ffor l u f = if l < u then f l; ffor (l+1) u f
let rec ffor l u f =
if l < u then ( f l; ffor (l+1) u f )
Type of f
int ! unit
let rec ffor l u f =
if l < u then ( f l; ffor (l+1) u f )
Template of f
{v:int|X1}!unit
let rec ffor l u f =
if l < u then ( f l; ffor (l+1) u f )
Template of f
{v:int|X1}!unit
Check: input l as required by f
let rec ffor l u f =
if l < u then ( f l; ffor (l+1) u f )
Template of f
{v:int|X1}!unit
l≺ u ⊢ {v:int|v=l} <: {v:int|X1}
l≺ u Æ v=l ) X1
Solution X1 = l≼ v Æ v≺ u
Reduces to
let rec ffor l u f =
if l < u then ( f l; ffor (l+1) u f )
Template of f
{v:int|X1}!unitSolution
X1 = l≼ v Æ v≺ u
Liquid Type of f
{v:int|l≼ v Æ v≺ u} ! unit
Closures / Higher-Order Functions
let rec ffor l u f = if l < u then f l; ffor (l+1) u f
Liquid Type of ffor
l:int!u:int!({v:int|l≼v≺u}!unit)!unit
Liquid Type of f
{v:int|l≼ v Æ v≺ u} ! unit
let rec ffor l u f = if l < u then f l; ffor (l+1) u f let min_index a = let r = ref 0 ffor 0 a.len (fun i
-> if a.(i) < a.(!r) then r := i ); !r
Min-Index Revisited
Liquid Type of fforl:int!u:int!({v:int|l≼v≺u}!unit)!unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of ffor 0u:int!({v:int|0≼v≺u}!unit)!unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of ffor 0 a.len
({v:int|0 ≼ v≺ a.len}!unit)!unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of ffor 0 a.len
({v:int|0 ≼ v≺ a.len}!unit)!unit
Template of (fun i ->...)
{v:int|Xi} ! unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of ffor 0 a.len
({v:int|0 ≼ v≺ a.len}!unit)!unit
Template of (fun i ->...)
{v:int|Xi} ! unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!rCheck: input (fun i... ) as required by
ffor
Liquid Type of ffor 0 a.len
({v:int|0 ≼ v≺ a.len}!unit)!unit
Template of (fun i ->...)
{v:int|Xi} ! unit
{Xi}!unit <:{0≼v≺a.len}!unit
Check: input (fun i... ) as required by ffor
{0≼v≺a.len}
unit{Xi} unit{Xi}!unit <:{0≼v≺a.len}!unit
{0≼v≺a.len} <:
{Xi}unit <: unit
Trivial
Function Subtyping Splits Into
Function Subtyping Splits Into
{Xi}!unit <:{0≼v≺a.len}!unit
0≼v≺ a.len ) Xi
0≼v≺ a.len ) Xi
Solution Xi = 0≼v≺ a.len
Solution Xi = 0≼v≺ a.lenTemplate of (fun i ->...)
{v:int|Xi} ! unit
Solution Xi = 0≼v≺ a.lenLiquid Type of (fun i ->...)
{v:int | 0≼v≺ a.len} ! unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of r{v:int | 0≼v≺ a.len} ref
Liquid Type of (fun i ->...)
{v:int | 0≼v≺ a.len} ! unit
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of r{v:int | 0≼v≺ a.len} ref
Access Safe
let min_index a = let r = ref 0 ffor 0 a.len (fun i -> if a.(i) < a.(!r) then r:= i)
!r
Liquid Type of r{v:int | 0≼v≺ a.len} ref
Liquid Type of min_indexa:int array ! {v:int | 0≼v≺ a.len}
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Collections (e.g. Lists)
Type of rangel:int ! u:int ! int list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Collections (e.g. Lists)
Template of rangel:int ! u:int ! {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Template of rangel:int ! u:int ! {v:int|Xout} list
Template of “then”: {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” and “tail” as required
Template of “then”: {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” and “tail” as requiredl≺ u ⊢ “head” <: {v:int|Xout}
Template of “then”: {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” and “tail” as requiredl≺ u ⊢ “head” <: {v:int|Xout}l≺ u ⊢ “tail” <: {v:int|Xout} list
Template of “then”: {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” and “tail” as requiredl≺ u ⊢ “head” <: {v:int|Xout}
let rec range l u = if l < u then l :: (range (l+1) u)
else []
l≺ u Æ v=l ) Xout
Reduces To(1)
Check: “head” and “tail” as requiredl≺ u ⊢ {v:int|v=l} <: {v:int|Xout}
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” and “tail” as requiredl≺ u ⊢ “tail” <: {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” and “tail” as requiredl≺ u ⊢ {Xout[l+1/l]} list <: {Xout }
list By List-Subtyping, Reduces Tol≺ u ⊢ {Xout[l+1/l]} <: {Xout
}
Check: “head” and “tail” as requiredl≺ u ⊢ {Xout[l+1/l]} list <: {Xout }
list Reduces To(2)l≺ u Æ Xout[l+1/l] ) Xout
(2)(1)
Solution Xout = l≼v≺ ul≺ u Æ v=l ) Xout
l≺ u Æ Xout[l+1/l] ) Xout
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Collections (e.g. Lists)
Template of rangel:int ! u:int ! {v:int|Xout} list
Solution Xout = l≼v≺ u
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Collections (e.g. Lists)
Liquid Type of rangel:int!u:int!{v:int|l≼v≺ u} list
Solution Xout = l≼v≺ u
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
let rec range l u = if l < u then l :: (range (l+1) u) else []
Generics/Polymorphism
let rec range l u = if l < u then l :: (range (l+1) u) else [] let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r)
0 indices
Generics/Polymorphism
Instance Typea = intb = int
Generic Type of fold_left(a ! b! a) ! a ! b list ! a
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Generic Type of fold_left(a ! b! a) ! a ! b list ! a
Instance Templatea = {v:int| Xa }b = {v:int| Xb }
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Instance Template of fold_left({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Instance Templatea = {v:int| Xa }b = {v:int| Xb }
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Template of r{v:int | Xa }
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Template of i{v:int | Xb }
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Template of min_indexa:int array ! {v:int | Xa }
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Check: args as required
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Check: args as required
1
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Check: args as required
12
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indicesInstance Template of fold_left
({Xa}!{Xb}!{Xa})!{Xa}!{Xb}list!{Xa}
Check: args as required
123
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Constraints
123
r:int!i:int!{v=i ∨ v=r} <: {Xa}!{Xb}!{Xa}
{v:int | v=0} <: {v:int | Xa}
{v:int | 0≼ v≺ a.len} list <: {v:int | Xb}
list
123
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Subtyping Reduces To Implications
123
r:int!i:int!{v=i ∨ v=r} <: {Xa}!{Xb}!{Xa}
{v:int | v=0} <: {v:int | Xa}
{v:int | 0≼ v≺ a.len} list <: {v:int | Xb}
list
Xa[r/v]Æ Xb[i/v]Æ (v=i ∨ v=r) ) Xav=0 ) Xa
0≼ v≺ a.len ) Xb
123
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Solution Xa , Xb = 0≼ v≺ a.len
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Solution Xa , Xb = 0≼ v≺ a.lenTemplates
r : {v:int|Xa}i : {v:int|Xb}
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Solution Xa , Xb = 0≼ v≺ a.lenLiquid Types
r : {v:int| 0≼ v≺ a.len}i : {v:int| 0≼ v≺ a.len}
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Solution Xa , Xb = 0≼ v≺ a.lenLiquid Types
r : {v:int| 0≼ v≺ a.len}i : {v:int| 0≼ v≺ a.len}
Safe
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Solution Xa , Xb = 0≼ v≺ a.lenTemplate of min_index
a:int array ! {v:int | Xa }
let min_index a = let indices = range 0 a.len fold_left (fun r i -> if a.(i) < a.(r) then i else r )
0 indices
Solution Xa , Xb = 0≼ v≺ a.lenLiquid Type of min_index
a:int array ! {v:int | 0≼v≺ a.len}
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Data (Structure) Properties
Piggyback Predicates On Types
[] ::
{x:int|0<x} listint list0<x Describes all elementsx:int
Representation
[] ::
0<xx:int
Type Unfolding
[] ::
0<hh:int
[] ::
0<xx:int
Head TailEmptyPositive Property holds recursivelyList of positive integers
[] ::
0<x Describes all elementsx:int
x<v v Describes tail elements
Representation
[] ::x<v
x:int
Type Unfolding
[] ::
h:int
[] ::x<v
x:int
Head TailEmptyElements larger than head Property holds recursively
List of sorted integers
h<v
Push Edge Predicate Into NodeRename Variable
h<x
Piggyback Predicates On Types
Data (Structure) Properties
[] ::
x:intUnfold
::
h:int
[] ::
x:int
l:sorted list h:int t:sorted
list & {h<x}
list
Instantiate
tl
match l with
h::t
x<Vx<V
h<x
Quantifier Instantiation
Piggyback Predicates On Types
Data (Structure) Properties
[] ::
x:intFold
h:int
[] ::
x:int
::
l:sorted list h:int t:sorted
list & {h<x}
listGeneralize
tllet l = h::t in
x<Vx<V
h<x
Quantifier Generalization
Another Example: Trees
Leaf
Node
x:int
x<VV<x
Node
Leaf
Node
x:int
Leaf
Node
x:int
Node
Leaf
x<VV<x x<VV<x
r<VV<r
Another Example: Trees
r:int
Unfold
< RHS nodesLHS nodes < root
Node
Leaf
Node
x:int
Leaf
Node
x:int
Node
Leaf
x<VV<x x<VV<x
r<xx<r
Another Example: Trees
r:int
Push Edge Predicates
Invariant: Binary Search Trees
Demoisort,bst
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Piggyback Predicates On Types
(Data) Structure Properties
measure len =| [] -> 0 | x::xs -> 1 + len xs
Representation: List Length
Representation: List Length
8l,x,xs. len([]) = 0 len(x::xs) = 1+len(xs)
Piggyback Predicates On Types
(Data) Structure Properties
l:’a listl:’a listh:’at:’a listlen(l)=1+len(t)
Instantiate
match l with
h::t
Quantifier Instantiation
8l,x,xs. len([]) = 0 len(x::xs) = 1+len(xs)
Piggyback Predicates On Types
(Data) Structure Properties
h:’at:’a list
Quantifier Generalization
8l,x,xs. len([]) = 0 len(x::xs) = 1+len(xs)
Generalize
let l = h::t in h:’at:’a listl:’a listlen(l)=1+len(t)
Demolistlen, msortb
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Leaf
l r
l = Left subtreer = Right subtree
treeHeight
H l = Left subtree’s heightH r = Right subtree’s height
measure H =
| Leaf = 0| Node(x,l,r) = 1 + max (H l) (H r)
Height Balanced Tree
|Hl–Hr|<2
Node
Height difference bounded at each node
Demoeval
Refinement Type InferenceBy Predicate Abstraction
0<x
[] ::
x:int
x<v
Automatic Inference
Predicates Determine InvariantLet X1, X2, ... = Unknown Predicates
Complex Subtyping Between data types
X1
X2
Reduces To Simple Implications Between X1, X2, ...
Solved by Predicate AbstractionOver atoms 0<⋆, ⋆<v, ...
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Program (ML) Verified Safety PropertiesList-based Sorting Sorted, Outputs Permutation of Input
Finite Map Balance, BST, Implements a SetRed-Black Trees Balance, BST, Color
Stablesort SortedExtensible Vectors Balance, Bounds Checking, …
Binary Heaps Heap, Returns Min, Implements SetSplay Heaps BST, Returns Min, Implements Set
Malloc Used and Free Lists Are AccurateBDDs Variable Order
Union Find AcyclicityBitvector Unification Acyclicity
Finite Maps (ML)5: ‘cat’
3: ‘cow’ 8: ‘tic’
1: ‘doc’ 4: ‘hog’ 7: ‘ant’ 9: ‘emu’From Ocaml Standard Library
Implemented as AVL TreesRotate/Rebalance on Insert/Delete
Verified InvariantsBinary Search Ordered
Height BalancedKeys Implement Set
Binary Decision Diagrams (ML)X1
X2 X2
X3
X4 X4
1
Graph-Based Boolean Formulas [Bryant 86]
X1ÛX2 Ù X3ÛX4 Efficient Formula Manipulation
Memoizing Results on SubformulasVerified Invariant
Variables Ordered Along Each Path
Vec: Extensible Arrays (317 LOC)
“Python-style” extensible arrays for Ocamlfind, insert, delete, join
etc.
Efficiency via balanced trees
Balanced
Height difference between siblings ≤ 2
Dsolve found balance violation
Debugging via Inference
Using Dsolve we found
Where imbalance occurred
(specific path conditions)
How imbalance occurred
(left tree off by up to 4)
Leading to test and fix
Part I
Part II
Part III
Refinement Types
Closures, Collections, Generics
Induction for Data Structures
Take Home LessonsWhy is Verification difficult?Complex “Invariants”
How to represent invariants? Factor into liquid type
How to infer liquid type?Subtyping + Predicate Abstraction
“Back-End” LogicConstraint Solving
Rich Decidable Logics Predicate Discovery…
Much Work Remains…
“Front-End” TypesDestructive Update
ConcurrencyObjects & Classes
Dynamic Languages…
Much Work Remains…
User InterfaceThe smarter your analysis,
the harder to tell why it fails!
Much Work Remains…
DetailsOcaml (Pure functional)[PLDI 08] “Liquid Types”
[PLDI 09] “Type-based Data-Structure Verification”
Constraint Solving [CAV 11] “Hindley-Milner-Cousots”
C (Pointers/Arithmetic, Aliasing, Mutation)[POPL 10] “Low-level Liquid Types”
POPL Lightning Talk/DemoOcaml (Pure functional)[PLDI 08] “Liquid Types”
[PLDI 09] “Type-based Data-Structure Verification”
Constraint Solving [CAV 11] “Hindley-Milner-Cousots”
C (Pointers/Arithmetic, Aliasing, Mutation)[POPL 10] “Low-level Liquid Types”
Patrick Rondon (PhD ’12)Demo + Lightning Talk
http://goto.ucsd.edu/liquidsource, papers, demo, etc.
Program Lines DMLAnnot.
LiquidAnnot.
LiquidTime(s)
dotprod 7 3 0 0.3bsearch 24 3 0 0.5queens 30 7 0 0.7
insersort 33 12 0 0.9tower 36 8 1 3.3
matmult 43 10 0 1.8heapsort 84 11 0 0.5
fft 107 13 1 9.1simplex 118 33 0 7.7
gauss 142 22 1 3.1Total 632 125 3 27.9
Evaluation: Array Bounds Checking
Evaluation: Array Bounds Checking
DML @ 20% LOCvs.
Liquid @ 0.5% LOC
The end
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Template of “then”-expression{v:int|Xout} list
Check: “head” and “tail” as requiredl≺ u ⊢ “head” <: {v:int|Xout}l≺ u ⊢ “tail” <: {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “head” as requiredl≺ u ⊢ {v:int|v=l} <: {v:int|Xout}
l≺ u Æ v=l ) Xout
Reduces To1
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required
Template of rangel:int ! u:int ! {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required
Template of range (l+1)u:int ! {v:int| Xout [l+1/l]} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required
Template of range (l+1) u{v:int| Xout [l+1/l][u/u]} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required
Template of range (l+1) u{v:int| Xout [l+1/l]} list
{Xout [l+1/l]} list <: then-“tail”
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required{Xout [l+1/l]} list <: then-“tail”
Template of “then”-expression{v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required{Xout [l+1/l]} list <: {Xout } list
Template of “then”-expression{v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required{Xout [l+1/l]} list <: {Xout } list
Template of rangel:int ! u:int ! {v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required{Xout [l+1/l]} list <: {Xout } list
Template of “then”-expression{v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required{Xout [l+1/l]} list <: {Xout } list
Template of “then”-expression{v:int|Xout} list
let rec range l u = if l < u then l :: (range (l+1) u)
else []
Check: “tail” as required{Xout [l+1/l]} list <: {Xout } list
Template of “then”-expression{v:int|Xout} list