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

a scala compiler plugin for verification
SMART_READER_LITE
LIVE PREVIEW

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


slide-1
SLIDE 1

a Scala Compiler Plugin for Verification

Nada Amin

slide-2
SLIDE 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
slide-3
SLIDE 3

Parity with Lab Verifier

  • @verify
  • assume / assert
  • loop invariants

@verify def counter(length: Int) { assume(length > 0) var i: Int = 0 var j: Int = 1 assert(j <= length) while(j < length) { i = j - 1 assert(j < length) while(i >= 0) { i = i - 1 } j = j + 1 } assert(j == length) }

slide-4
SLIDE 4

Design by Contract

  • precondition
  • assume when verifying
  • assert when calling
  • postcondition
  • assert when verifying
  • assume when calling

@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 } @verify def odd(n: Int): Int = { precondition(n >= 0) var r = 0 if (n == 0) r = 0 else if (n == 1) r = 1 else r = even(n-1) postcondition(r == 0 || r == 1) r }

slide-5
SLIDE 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 }

slide-6
SLIDE 6

Simulating Havoc

  • bject 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 } }

slide-7
SLIDE 7

Partial Understanding

@verify def test(queue: Queue) { var l = 0 var oldi = 0 var newi = 0 var q = queue do { l = lock(l)

  • ldi = 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))

slide-8
SLIDE 8

Partial Understanding

@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 } class Queue(var data: Int, var next: Option[Queue])

slide-9
SLIDE 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) } }

slide-10
SLIDE 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 ==

  • ld_savings_balance + old_checking_balance &&

savings.balance >= 0 && checking.balance >= 0) } }

slide-11
SLIDE 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

slide-12
SLIDE 12

Eldarica

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

slide-13
SLIDE 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
slide-14
SLIDE 14

False Negative

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

slide-15
SLIDE 15

False Positive

warning: ignoring any aliasing involving other successfully verified withdraw with 1 warning(s)

@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

  • ther.balance = 0 // other could be this!

postcondition(balance == old_balance - amount && balance >= 0) } }

slide-16
SLIDE 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.

slide-17
SLIDE 17

Questions?