1 / 177

Liquid Types

Liquid Types. Pat Rondon Ming Kawaguchi Ranjit Jhala. Goal: Software Verification. Verify absence of run-time errors. Buffer overflows Deadlocks Assertions. Progress: Path Sensitive Analyses. SMT Solvers Path Predicates Model Checking Loop Invariants, Function Summaries.

seda
Download Presentation

Liquid Types

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

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

More Related