liquid types n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Liquid Types PowerPoint Presentation
Download Presentation
Liquid Types

play fullscreen
1 / 177

Liquid Types

121 Views Download Presentation
Download Presentation

Liquid Types

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript

  1. Liquid Types Pat Rondon Ming Kawaguchi Ranjit Jhala

  2. Goal: Software Verification Verify absence of run-time errors • Buffer overflows • Deadlocks • Assertions

  3. Progress: Path Sensitive Analyses • SMT Solvers Path Predicates • Model Checking Loop Invariants, Function Summaries

  4. Progress: Path Sensitive Analyses • SMT Solvers Path Predicates • Model Checking Loop Invariants, Function Summaries • ASTREE • SLAM • BLAST Device Drivers • SATURN Linux Kernel

  5. Imprecise, Limited Applicability • ASTREE • SLAM • BLAST Device Drivers • SATURN Linux Kernel • Control-intensive • Properties • Null-pointers • Double-locks …

  6. Imprecise, Limited Applicability ? • Control-intensive • Properties • Null-pointers • Double-locks …

  7. The Sources of Imprecision • Complex Data • Arrays • Lists • Hash Tables … • Complex Control • Function Pointers • Closures • Callbacks … ? • SLAM • BLAST Device Drivers • SATURN Linux Kernel

  8. Types, Data and Control • “Since the 70s, • typeshave dealt with • dataand control” • Complex Data • Arrays • Lists • Hash Tables … • Complex Control • Function Pointers • Closures • Callbacks …

  9. Types and Complex Data • Complex Data • Arrays • Lists • Hash Tables … • Quantified Predicates • Forall x in array: … • Forall x in list: … • Hard to automate

  10. Types and Complex Data • Complex Data • Arrays • Lists • Hash Tables … • Quantified Predicates • Forall x in array: … • Forall x in list: … • Hard to automate Forall x in list: x is an int int list

  11. Types and Complex Control • Complex Control • Function Pointers • Closures • Callbacks … • Function Summaries • Pre/Post Conditions • … are insufficient

  12. Types and Complex Control (’a!’b)!’alist!’blist Higher-Order Summaries • Complex Control • Function Pointers • Closures • Callbacks … • Function Summaries • Pre/Post Conditions • … are insufficient

  13. SMT and Model Checking Path and value information x>0, flag=1 Complex Data and Control

  14. Type Systems • Complex Data and Control • int list • (’a!’b)!’alist!’blist Path and value information

  15. Combine Strengths Path and value information Data Structures • Precise Software Verification

  16. Plan • Motivation • Combining Types and Predicates

  17. Combining Types and Predicates Refinement Types Typesrefinedwith Predicates over values

  18. RefinementTypes positive integers • {V:int|0<V} Type Refinement

  19. RefinementTypes integers between i,j {V:int|i·VÆV·j} Type Refinement

  20. RefinementTypes list of integers between i,j {V:int|i<VÆV<j}list Type Refinement

  21. RefinementTypes function with positive input i output larger than input i:{V:int|0<V}!{V:int|i<V} “Pre” “Post” “Requires” “Ensures”

  22. Verification using Refinement Types • let abs x = • ifx>0 thenx else -x • lettrunci n = • leti’ = abs iin • let n’ = abs n in • ifi’<=n’ thenielsen’*(divii’) Divide by zero?

  23. Verification using Refinement Types • let abs x = • ifx>0 thenx else -x • lettrunci n = • leti’ = abs iin • let n’ = abs n in • ifi’<=n’ thenielsen’*(divii’) • div::int!{V:int|V0}!int

  24. Verification using Refinement Types • let abs x = • ifx>0 thenx else -x • lettrunci n = • leti’ = abs iin • let n’ = abs n in • ifi’<=n’ thenielsen’*(divii’) • div::int!{V:int|V0}!int Typecheck implies i’is nonzero

  25. Verification using Refinement Types • letarraysum a = • let recloop m i n = • ifi >= n then m else • leti’= i + 1 in • let m’= m + (get a i) in • loop m’ i’ n in • loop 0 0 (length a) Array index within bounds?

  26. Verification using Refinement Types • letarraysum a = • let recloop m i n = • ifi >= n then m else • leti’= i + 1 in • let m’= m + (get a i) in • loop m’ i’ n in • loop 0 0 (length a) get::x:’aarray!{V:int|0<=V<lengthx}!’a

  27. Verification using Refinement Types • letarraysum a = • let recloop m i n = • ifi >= n then m else • leti’= i + 1 in • let m’= m + (get a i) in • loop m’ i’ n in • loop 0 0 (length a) get:: x:’aarray!{V:int|0<=V<lengthx}!’a Typecheck implies iwithin bounds

  28. Verification using Refinement Types Just one little problem… How to compute Refinement Types?

  29. How to compute Refinement Types? • Automatic Generation? • undecidable: space of types is unbounded • Manual Specification? • “The more interesting your types get, the less fun it is to write them down.” • - Benjamin Pierce

  30. Dependent ML [Pfenning-Xi 1998] let rec helper (v1, v2, i, n, sum) = ifi= n then sum else helper (v1, v2, i+1, n, sum + (get v1 i) * (get v2 i))‏ letdotprod(v1, v2) = helper (v1, v2, 0, length v1, 0)‏ ‏withtype{n:nat, i:nat | i <= n} => int array(n) * int array(n) * int(i) * int(n) * int -> int withtype{n:nat} => int array(n) * int array(n) -> int Programmer writes type annotations (like @requires, @ensures, @invariant)

  31. ... A Lot of Annotations fun norm (arr2, n, i, j) = let val c = sub2 (arr2, i, j)‏ in norm_aux (arr2, n, i, c, 1)‏ end withtype {m:pos,n:pos,i:pos,j:pos | i < m, j < n} <> => (float array(n)) array(m) * int(n) * int(i) * int(j) -> unit fun row_op_aux1 (arr2, n, i, i', c, j) = if j < n then let valcj = sub2 (arr2, i, j)‏ valcj' = sub2 (arr2, i', j)‏ val _ = update2 (arr2, i', j, cj' -. cj *. c)‏ in row_op_aux1 (arr2, n, i, i', c, j+1)‏ end else ()‏ withtype {m:pos,n:pos,i:pos,i':nat, j:pos | i < m, i' < m, j <= n} <n-j> => (float array(n)) array(m) * int(n) * int(i) * int(i') * float * int(j) -> unit fun row_op_aux2 (arr2, n, i, i', j) = let val c' = sub2 (arr2, i', j)‏ in row_op_aux1 (arr2, n, i, i', c', 1)‏ end withtype {m:pos,n:pos,i:pos,i':nat, j:pos | i < m, i' < m, j < n} <> => (float array(n)) array(m) * int(n) * int(i) * int(i') * int(j) -> unit fun row_op_aux3 (arr2, m, n, i, j, i') = if i' < m then if i' <> i then let val _ = row_op_aux2(arr2, n, i, i', j)‏ in row_op_aux3 (arr2, m, n, i, j, i'+1)‏ end else row_op_aux3 (arr2, m, n, i, j, i'+1)‏ else ()‏ withtype {m:pos,n:pos,i:pos,j:pos,i':nat | i < m, j < n, i' <= m} <m-i'> => (float array(n)) array(m) * int(m) * int(n) * int(i) * int(j) * int(i') -> unit fun row_op (arr2, m, n, i, j) = let val _ = norm (arr2, n, i, j)‏ in row_op_aux3 (arr2, m, n, i, j, 0)‏ end withtype {m:pos,n:pos,i:pos,j:pos| i < m, j < n} <> => (float array(n)) array(m) * int(m) * int(n) * int(i) * int(j) -> unit fun simplex (arr2, m, n) = if is_neg (arr2, n) then if unb1 (arr2, m, n, 0, 1) then abort ("simplex: unbound solution!")‏ else let val j = enter_var (arr2, n, 1, sub2 (arr2, 0, 1), 2)‏ val (i, r) = init_ratio (arr2, m, n, j, 1)‏ vali = depart_var (arr2, m, n, j, i, r, i+1)‏ val _ = row_op (arr2, m, n, i, j)‏ in simplex (arr2, m, n)‏ end else ()‏ withtype {m:int,n:int | m > 1, n > 2} (float array(n)) array(m) * int(m) * int(n) -> unit fun main (A (arr2, m, n)) = if m > 1 then if n > 2 then simplex (arr2, m, n)‏ else abort ("too few columns")‏ else abort ("too few rows")‏ withtype float array2D -> unit fun('a) nRows (A (_, m, _)) = m withtype {m:nat,n:nat} <> => 'a array2D(m,n) -> int(m)‏ fun('a) nCols (A (_, _, n)) = n withtype {m:nat,n:nat} <> => 'a array2D(m,n) -> int(n)‏ fun is_neg_aux (arr2, n, j) = if j < n - 1 then if sub2 (arr2, 0, j) <. 0.0 then true else is_neg_aux (arr2, n, j+1) else false withtype {m:pos,n:pos,j:nat | j <= n} <n-j> => (float array(n)) array(m) * int(n) * int(j) -> bool fun is_neg (arr2, n) = is_neg_aux (arr2, n, 1)‏ withtype {m:pos,n:pos} <> => (float array(n)) array(m) * int(n) -> bool fun unb1 (arr2, m, n, i, j) = if j < n-1 then if sub2 (arr2, 0, j) <. 0.0 then unb2 (arr2, m, n, i+1, j)‏ else unb1 (arr2, m, n, 0, j+1)‏ else false withtype {m:pos,n:pos,i:nat,j:nat | i < m, j <= n} <n-j, m-i> => (float array(n)) array(m) * int (m) * int(n) * int(i) * int(j) -> bool and unb2 (arr2, m, n, i, j) = if i < m then if sub2 (arr2, i, j) <. 0.0 then unb2 (arr2, m, n, i+1, j)‏ else unb1 (arr2, m, n, 0, j+1)‏ else true withtype {m:pos,n:pos,i:nat,j:nat | i <= m, j < n} <n-j,m-i> => (float array(n)) array(m) * int (m) * int(n) * int(i) * int(j) -> bool fun enter_var (arr2, n, j, c, j') = if j' < n-1 then let val c' = sub2 (arr2, 0, j')‏ in if c' <. c then enter_var (arr2, n, j', c', j'+1)‏ else enter_var (arr2, n, j, c, j'+1)‏ end else j withtype {m:pos,n:pos,j:pos,j':pos | j+1 < n, j' < n} <n-j'> => (float array(n)) array(m) * int(n) * int(j) * float * int(j') -> [j:pos | j+1 < n] int(j)‏ fun depart_var (arr2, m, n, j, i, r, i') = if i' < m then let val c' = sub2 (arr2, i', j)‏ in if c' >. 0.0 then let val r' = sub2(arr2, i', n-1) /. c' in if r' <. r then depart_var(arr2, m, n, j, i', r', i'+1)‏ else depart_var (arr2, m, n, j, i, r, i'+1)‏ end else depart_var (arr2, m, n, j, i, r, i'+1)‏ end else i withtype {m:pos,n:pos,i:pos,i':pos,j:pos | i < m, i' <= m, j < n} <m-i'> => (float array(n)) array(m) * int(m) * int(n) * int(j) * int(i) * float * int(i') -> [i:pos | i < m] int(i)‏ fun init_ratio (arr2, m, n, j, i) = if i < m then let val c = sub2 (arr2, i, j)‏ in if c >. 0.0 then (i, sub2 (arr2, i, n-1) /. c)‏ else init_ratio (arr2, m, n, j, i+1)‏ end else abort ("init_ratio: negative coefficients!")‏ withtype {m:pos,n:pos,j:pos,i:pos | j < n, i <= m} <m-i> => (float array(n)) array(m) * int(m) * int(n) * int(j) * int(i) -> [i:pos | i < m] int(i) * float Simplex Algorithm

  32. … A Lot of Annotations fun norm (arr2, n, i, j) = let val c = sub2 (arr2, i, j)‏ in norm_aux (arr2, n, i, c, 1)‏ end withtype {m:pos,n:pos,i:pos,j:pos | i < m, j < n} <> => (float array(n)) array(m) * int(n) * int(i) * int(j) -> unit fun row_op_aux1 (arr2, n, i, i', c, j) = if j < n then let valcj = sub2 (arr2, i, j)‏ valcj' = sub2 (arr2, i', j)‏ val _ = update2 (arr2, i', j, cj' -. cj *. c)‏ in row_op_aux1 (arr2, n, i, i', c, j+1)‏ end else ()‏ withtype {m:pos,n:pos,i:pos,i':nat, j:pos | i < m, i' < m, j <= n} <n-j> => (float array(n)) array(m) * int(n) * int(i) * int(i') * float * int(j) -> unit fun row_op_aux2 (arr2, n, i, i', j) = let val c' = sub2 (arr2, i', j)‏ in row_op_aux1 (arr2, n, i, i', c', 1)‏ end withtype {m:pos,n:pos,i:pos,i':nat, j:pos | i < m, i' < m, j < n} <> => (float array(n)) array(m) * int(n) * int(i) * int(i') * int(j) -> unit fun row_op_aux3 (arr2, m, n, i, j, i') = if i' < m then if i' <> i then let val _ = row_op_aux2(arr2, n, i, i', j)‏ in row_op_aux3 (arr2, m, n, i, j, i'+1)‏ end else row_op_aux3 (arr2, m, n, i, j, i'+1)‏ else ()‏ withtype {m:pos,n:pos,i:pos,j:pos,i':nat | i < m, j < n, i' <= m} <m-i'> => (float array(n)) array(m) * int(m) * int(n) * int(i) * int(j) * int(i') -> unit fun row_op (arr2, m, n, i, j) = let val _ = norm (arr2, n, i, j)‏ in row_op_aux3 (arr2, m, n, i, j, 0)‏ end withtype {m:pos,n:pos,i:pos,j:pos| i < m, j < n} <> => (float array(n)) array(m) * int(m) * int(n) * int(i) * int(j) -> unit fun simplex (arr2, m, n) = if is_neg (arr2, n) then if unb1 (arr2, m, n, 0, 1) then abort ("simplex: unbound solution!")‏ else let val j = enter_var (arr2, n, 1, sub2 (arr2, 0, 1), 2)‏ val (i, r) = init_ratio (arr2, m, n, j, 1)‏ vali = depart_var (arr2, m, n, j, i, r, i+1)‏ val _ = row_op (arr2, m, n, i, j)‏ in simplex (arr2, m, n)‏ end else ()‏ withtype {m:int,n:int | m > 1, n > 2} (float array(n)) array(m) * int(m) * int(n) -> unit fun main (A (arr2, m, n)) = if m > 1 then if n > 2 then simplex (arr2, m, n)‏ else abort ("too few columns")‏ else abort ("too few rows")‏ withtype float array2D -> unit fun('a) nRows (A (_, m, _)) = m withtype {m:nat,n:nat} <> => 'a array2D(m,n) -> int(m)‏ fun('a) nCols (A (_, _, n)) = n withtype {m:nat,n:nat} <> => 'a array2D(m,n) -> int(n)‏ fun is_neg_aux (arr2, n, j) = if j < n - 1 then if sub2 (arr2, 0, j) <. 0.0 then true else is_neg_aux (arr2, n, j+1) else false withtype {m:pos,n:pos,j:nat | j <= n} <n-j> => (float array(n)) array(m) * int(n) * int(j) -> bool fun is_neg (arr2, n) = is_neg_aux (arr2, n, 1)‏ withtype {m:pos,n:pos} <> => (float array(n)) array(m) * int(n) -> bool fun unb1 (arr2, m, n, i, j) = if j < n-1 then if sub2 (arr2, 0, j) <. 0.0 then unb2 (arr2, m, n, i+1, j)‏ else unb1 (arr2, m, n, 0, j+1)‏ else false withtype {m:pos,n:pos,i:nat,j:nat | i < m, j <= n} <n-j, m-i> => (float array(n)) array(m) * int (m) * int(n) * int(i) * int(j) -> bool and unb2 (arr2, m, n, i, j) = if i < m then if sub2 (arr2, i, j) <. 0.0 then unb2 (arr2, m, n, i+1, j)‏ else unb1 (arr2, m, n, 0, j+1)‏ else true withtype {m:pos,n:pos,i:nat,j:nat | i <= m, j < n} <n-j,m-i> => (float array(n)) array(m) * int (m) * int(n) * int(i) * int(j) -> bool fun enter_var (arr2, n, j, c, j') = if j' < n-1 then let val c' = sub2 (arr2, 0, j')‏ in if c' <. c then enter_var (arr2, n, j', c', j'+1)‏ else enter_var (arr2, n, j, c, j'+1)‏ end else j withtype {m:pos,n:pos,j:pos,j':pos | j+1 < n, j' < n} <n-j'> => (float array(n)) array(m) * int(n) * int(j) * float * int(j') -> [j:pos | j+1 < n] int(j)‏ fun depart_var (arr2, m, n, j, i, r, i') = if i' < m then let val c' = sub2 (arr2, i', j)‏ in if c' >. 0.0 then let val r' = sub2(arr2, i', n-1) /. c' in if r' <. r then depart_var(arr2, m, n, j, i', r', i'+1)‏ else depart_var (arr2, m, n, j, i, r, i'+1)‏ end else depart_var (arr2, m, n, j, i, r, i'+1)‏ end else i withtype {m:pos,n:pos,i:pos,i':pos,j:pos | i < m, i' <= m, j < n} <m-i'> => (float array(n)) array(m) * int(m) * int(n) * int(j) * int(i) * float * int(i') -> [i:pos | i < m] int(i)‏ fun init_ratio (arr2, m, n, j, i) = if i < m then let val c = sub2 (arr2, i, j)‏ in if c >. 0.0 then (i, sub2 (arr2, i, n-1) /. c)‏ else init_ratio (arr2, m, n, j, i+1)‏ end else abort ("init_ratio: negative coefficients!")‏ withtype {m:pos,n:pos,j:pos,i:pos | j < n, i <= m} <m-i> => (float array(n)) array(m) * int(m) * int(n) * int(j) * int(i) -> [i:pos | i < m] int(i) * float Simplex Algorithm

  33. 30% of code

  34. Abstract MC + Type Inference Goal

  35. How to compute Refinement Types? • 1. Restrict space of types • Liquid Types • 2. Search space (efficiently) • Liquid Type Inference

  36. Plan • Motivation • Combining Types and Predicates

  37. Plan • Motivation • Liquid Types

  38. Logically Qualified Types Logical Qualifiers: 0 ·V F·V V·F V ·lengthF F= “wildcard” instantiate with any program variable Liquid Types: Types refined with conjunctions of qualifiers

  39. Liquid Type Logical Qualifiers: 0 ·V F·V V·F V ·lengthF • letrec sum n = • if n <= 0 then 0 else • lets= sum (n-1) in • s + n sum:: n:int!{V:int|0·VÆn·V}

  40. The Liquid Restriction Liquid Refinements = conjunctions of qualifiers Finite number of qualifiers )Finite space of possible types Inference = (efficiently) search finite space!

  41. Plan • Motivation • Liquid Types • Liquid Type Inference • Complex Control • Complex Data • Results

  42. Liquid Type Inference Remember these: If Alice doubles her age, she would still be 10 years younger than Bob, who was born in 1952. How old are Alice and Bob ? Algorithm: Step 1: Templates for unknowns Step 2: Constraints on templates Step 3: Solve constraints Alice’s age: a Bob’sage:b = 23 = 56 2a = b– 10 b = 2008 - 1952

  43. Liquid Type Inference Step 1: Templates for unknowns Step 2: Constraints on templates Step 3: Solve constraints

  44. Step 1: Templates Liquid Type refines ML Type • letrec sum n = • if n < 0 then 0 else • lets= sum (n-1) in • s + n ML Type n:int!int via Hindley-Milner Type Inference

  45. Step 1: Templates Liquid Type refines ML Type • letrec sum n = • if n < 0 then 0 else • lets= sum (n-1) in • s + n ML Type Liquid Type Template n:int!int n:{V:int|?}!{V:int|?} n:{V:int|K1}!{V:int|K2}

  46. Step 1: Templates Liquid Type refines ML Type Liquid Type Variables for unknown refinements Template n:{V:int|K1}!{V:int|K2}

  47. Liquid Type Inference Step 1: Templates for unknowns Step 2: Constraints on templates Step 3: Solve constraints

  48. Step 2: Constraints Two kinds of constraints: • Scope • Value Flow

  49. Step 2: Scope Constraints Which variables can appear in type Template sum:: n:{V:int|K1}!{V:int|K2} • letrec sum n = • if n < 0 then 0 else • lets= sum (n-1) in • s + n (S1) ;`K1 No variables in scope, no variables in K1

  50. Step 2: Scope Constraints Which variables can appear in type Template sum:: n:{V:int|K1}!{V:int|K2} • letrec sum n = • if n < 0 then 0 else • lets= sum (n-1) in • s + n (S2) n:int`K2 Only n in scope, n can appear in K2