a scala compiler plugin for verification
play

a Scala Compiler Plugin for Verification Nada Amin Features - PowerPoint PPT Presentation

a Scala Compiler Plugin for Verification Nada Amin Features transform Scala def AST into a CFG front-end to lab verifier front-end to Eldarica support for design by contract partial understanding verified classes


  1. a Scala Compiler Plugin for Verification Nada Amin

  2. Features • transform Scala def AST into a CFG • front-end to lab verifier • front-end to Eldarica • support for • design by contract • partial understanding • verified classes

  3. Parity with Lab Verifier @verify def counter(length: Int) { assume(length > 0) var i: Int = 0 • @verify var j: Int = 1 assert(j <= length) while(j < length) { • assume / assert i = j - 1 assert(j < length) • loop invariants while(i >= 0) { i = i - 1 } j = j + 1 } assert(j == length) }

  4. Design by Contract @verify def even(n: Int): Int = { • precondition precondition(n >= 0) var r = 0 if (n == 0) r = 1 • assume when verifying else if (n == 1) r = 0 else r = odd(n-1) postcondition(r == 0 || r == 1) • assert when calling r } • postcondition @verify def odd(n: Int): Int = { precondition(n >= 0) • assert when verifying var r = 0 if (n == 0) r = 0 else if (n == 1) r = 1 • assume when calling else r = even(n-1) postcondition(r == 0 || r == 1) r }

  5. Design by Contract @verify def even(n: Int): Int = { precondition(n >= 0) var r = 0 if (n == 0) r = 1 else if (n == 1) r = 0 else r = odd(n-1) postcondition(r == 0 || r == 1) r }

  6. Simulating Havoc object ok { val random = new scala.util.Random() def havoc = random.nextInt - random.nextInt @verify def partition(b: Int) = { precondition(b >= 0) var P1:Int = 0 var P2:Int = 0 var a: Int = b var h: Int = 0 while(a > 0) { h = havoc if(h >= 0) P1 = P1 + 1 else P2 = P2 + 1 a = a - 1 } assert(P1 + P2 == b) val result = P1 postcondition(result <= b) result } }

  7. Partial Understanding @verify def test(queue: Queue) { var l = 0 var oldi = 0 var newi = 0 var q = queue do { l = lock (l) oldi = newi if (q.next != None) { q = q.next.get q.data = newi l = unlock (l) newi += 1 } } while (newi != oldi) l = unlock (l) } ((l == 0 && newi != oldi) || (l == 1 && newi == oldi))

  8. Partial Understanding class Queue(var data: Int, var next: Option[Queue]) @verify def lock(l: Int) = { precondition(l == 0) val r = 1 postcondition(r == 1) r } @verify def unlock(l: Int) = { precondition(l == 1) val r = 0 postcondition(r == 0) r }

  9. Verified Classes @verify class BankAccount { var balance = 0 @verify def withdraw(amount: Int) { precondition(amount >= 0 && balance >= amount) val old_balance = old(balance) balance -= amount postcondition( balance == old_balance - amount && balance >= 0) } @verify def deposit(amount: Int) { precondition(amount >= 0 && balance >= 0) val old_balance = old(balance) balance += amount postcondition(balance == old_balance + amount && balance >= 0) } }

  10. Verified Classes @verify class DoubleBankAccount { val savings = new BankAccount val checking = new BankAccount @verify def transfer(amount: Int) { precondition(amount >= 0 && savings.balance >= amount && checking.balance >= 0) val old_savings_balance = old(savings.balance) val old_checking_balance = old(checking.balance) savings.withdraw(amount) checking.deposit(amount) postcondition(savings.balance + checking.balance == old_savings_balance + old_checking_balance && savings.balance >= 0 && checking.balance >= 0) } }

  11. Eldarica • Predicate abstraction and interpolation • If it terminates: • Either successfully verifies CFG deriving loop invariants • Or finds a counterexample • Used mostly as a black-box • I extended it to show step-by-step trace for counterexample

  12. Eldarica Model for counterexample: == step 1 == @verify length ← 2 def test(length: Int) { == step 2 == assume(length > 0) i ← 0 var i: Int = 0 == step 3 == var j: Int = 1 j ← 1 while(j < length) { == step 4 == i = j - 1 i ← -1 while(i >= 0) { == step 5 == i = i - 1 j ← 3 } == final values == j = j + 2 // should be 1 i ← -1 } j ← 3 assert(j == length) } length ← 2 The program has a bug in asserting: (j == length)

  13. Limitations • false negatives due to partial understanding • false positives due to aliasing of verified classes • aliasing can also cause false negatives • severe limitations for verified classes • no support for recursive ADTs • underlying limitations of theorem prover

  14. False Negative def alwaysTrue() = true Model for counterexample: @verify def test() = { == step 1 == var x = 0 x ← 0 if (alwaysTrue()) { == step 2 == x += 1 x ← -1 } == final values == if (alwaysTrue()) { x ← -1 x -= 1 } The program has a bug in asserting: (x == 0) postcondition(x == 0) x }

  15. False Positive @verify class BankAccount { var balance = 0 @verify def withdraw(amount: Int, other: BankAccount ) { precondition(amount >= 0 && balance >= amount) val old_balance = old(balance) balance -= amount other.balance = 0 // other could be this! postcondition(balance == old_balance - amount && balance >= 0) } } warning: ignoring any aliasing involving other successfully verified withdraw with 1 warning(s)

  16. Conclusion • In retrospect, it seems like a bad idea to introduce features that lead to soundness holes. The trade-off might be acceptable: the holes are well-delimited. • Leon takes a different approach: soundly verify a functional subset of Scala.

  17. Questions?

Recommend


More recommend