K. Rustan M. Leino RiSE, Joint work with: Peter Müller (ETH Zurich) Jan Smans (KU Leuven) Special thanks to Mike Barnett VMCAI, Madrid, Spain, 18 January.

Slides:



Advertisements
Similar presentations
1 Lecture 5 Towards a Verifying Compiler: Multithreading Wolfram Schulte Microsoft Research Formal Methods 2006 Race Conditions, Locks, Deadlocks, Invariants,
Advertisements

Joint work with Mike Barnett, Robert DeLine, Manuel Fahndrich, and Wolfram Schulte Verifying invariants in object-oriented programs K. Rustan M. Leino.
Writing specifications for object-oriented programs K. Rustan M. Leino Microsoft Research, Redmond, WA, USA 21 Jan 2005 Invited talk, AIOOL 2005 Paris,
1 Towards a Verifying Compiler: The Spec# Approach Wolfram Schulte Microsoft Research Formal Methods 2006 Joint work with Rustan Leino, Mike Barnett, Manuel.
Automated Theorem Proving Lecture 1. Program verification is undecidable! Given program P and specification S, does P satisfy S?
Lecture 4 Towards a Verifying Compiler: Data Abstraction Wolfram Schulte Microsoft Research Formal Methods 2006 Purity, Model fields, Inconsistency _____________.
Challenges in increasing tool support for programming K. Rustan M. Leino Microsoft Research, Redmond, WA, USA 23 Sep 2004 ICTAC Guiyang, Guizhou, PRC joint.
In this episode of The Verification Corner, Rustan Leino talks about Loop Invariants. He gives a brief summary of the theoretical foundations and shows.
Semantics Static semantics Dynamic semantics attribute grammars
Copyright , Doron Peled and Cesare Tinelli. These notes are based on a set of lecture notes originally developed by Doron Peled at the University.
Constraint Semantics for Abstract Read Permissions 28 th July 2014, FTfJP, Uppsala John Tang Boyland (UW-Milwaukee/ETH Zurich) Peter Müller, Malte Schwerhoff,
CSI 3120, Implementing subprograms, page 1 Implementing subprograms The environment in block-structured languages The structure of the activation stack.
Automated Software Verification with a Permission-Based Logic 20 th June 2014, Zürich Malte Schwerhoff, ETH Zürich.
Verification of Multithreaded Object- Oriented Programs with Invariants Bart Jacobs, K. Rustan M. Leino, Wolfram Schulte.
A simple sequential reasoning approach for sound modular verification of mainstream multithreaded programs Wolfram Schulte & Bart Jacobs Microsoft Research.
Chapter 6: Process Synchronization
K. Rustan M. Leino Microsoft Research Peter Müller ETH Zurich Angela Wallenburg Chalmers University.
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond, WA, USA 3 December 2008 U. Lugano Lugano, Switzerland.
Copyright © 2006 Addison-Wesley. All rights reserved.1-1 ICS 410: Programming Languages Chapter 3 : Describing Syntax and Semantics Axiomatic Semantics.
ISBN Chapter 3 Describing Syntax and Semantics.
Compiler Construction
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond, WA part 0 International Summer School Marktoberdorf Marktoberdorf,
Fractional Permissions without the Fractions Alex Summers ETH Zurich Joint work with: Stefan Heule, Rustan Leino, Peter Müller ETH Zurich MSR Redmond ETH.
ECI 2007: Specification and Verification of Object-Oriented Programs Lecture 2 Courtesy: K. Rustan M. Leino and Wolfram Schulte.
Lecture 2 Towards a Verifying Compiler: Logic of Object oriented Programs Wolfram Schulte Microsoft Research Formal Methods 2006 Objects, references, heaps,
ESC Java. Static Analysis Spectrum Power Cost Type checking Data-flow analysis Model checking Program verification AutomatedManual ESC.
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond, WA part 0 Summer School on Logic and Theorem-Proving in Programming.
K. Rustan M. Leino RiSE, Microsoft Research Typing, Analysis and Verification of Heap-Manipulating Programs Dagstuhl, Germany 20 July 2009.
Encapsulation by Subprograms and Type Definitions
Building a program verifier K. Rustan M. Leino Microsoft Research, Redmond, WA 10 May 2006 Guest lecture, Shaz Qadeer’s cse599f, Formal Verification of.
Language Support for Lightweight transactions Tim Harris & Keir Fraser Presented by Narayanan Sundaram 04/28/2008.
ESC Java. Static Analysis Spectrum Power Cost Type checking Data-flow analysis Model checking Program verification AutomatedManual ESC.
Well-cooked Spaghetti: Weakest-Precondition of Unstructured Programs Mike Barnett and Rustan Leino Microsoft Research Redmond, WA, USA.
Describing Syntax and Semantics
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond Caltech Pasadena, CA 12 November 2009.
On the Duality of Operating System Structures Hugh C. Lauer Xerox Corporation Roger M. Needham Cambridge University Presented By: Ashwini Kulkarni.
1/25 Pointer Logic Changki PSWLAB Pointer Logic Daniel Kroening and Ofer Strichman Decision Procedure.
K. Rustan M. Leino RiSE, Microsoft Research, Redmond joint work with Peter Müller and Jan Smans Lecture 0 1 September 2009 FOSAD 2009, Bertinoro, Italy.
Refinement, reusable libraries, instantiable classes K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond Joint work.
Using and Building an Automatic Program Verifier K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond Lecture 3 Marktoberdorf.
Viper A Verification Infrastructure for Permission-Based Reasoning 1 st March 2015, ECOOP’15 PC Meeting, Zurich Uri Juhasz, Ioannis Kassios, Peter Müller,
Program Verification K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond University of Washington CSE P January.
Viper A Verification Infrastructure for Permission-Based Reasoning 24 th March 2015, JML Workshop, Leiden Uri Juhasz, Ioannis Kassios, Peter Müller, Milos.
Rustan Leino RiSE, Microsoft Research, Redmond MIT 5 June 2009 Joint work with: Peter Müller, ETH Zurich Jan Smans, KU Leuven.
An Object-Oriented Approach to Programming Logic and Design Chapter 3 Using Methods and Parameters.
Verification of Programs with Inspector Methods Bart Jacobs and Frank Piessens Dept. CS, K.U.Leuven, Belgium.
A Universe-Type-Based Verification Technique for Mutable Static Fields and Methods Alexander J Summers Sophia Drossopoulou Imperial College London Peter.
Specifying and verifying programs in Spec# K. Rustan M. Leino Microsoft Research, Redmond, WA, USA Invited talk, PSI 2006 Novosibirsk, Russia 27 June 2006.
13-1 Chapter 13 Concurrency Topics Introduction Introduction to Subprogram-Level Concurrency Semaphores Monitors Message Passing Java Threads C# Threads.
CS533 – Spring Jeanie M. Schwenk Experiences and Processes and Monitors with Mesa What is Mesa? “Mesa is a strongly typed, block structured programming.
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond, WA part 2 International Summer School Marktoberdorf Marktoberdorf,
An Introduction to Automated Program Verification with Permission Logics 15 th May 2015, Systems Group, ETH Zurich Uri Juhasz, Ioannis Kassios, Peter Müller,
K. Rustan M. Leino RiSE, Microsoft Research, Redmond joint work with Peter Müller and Jan Smans Lecture 1 2 September 2009 FOSAD 2009, Bertinoro, Italy.
VSTTE’12 29 th January 2012, Philadelphia Comparing Verification Condition Generation with Symbolic Execution Malte Schwerhoff, ETH Zurich Joint work with.
Spec# John Lefor Program Manager Developer Division, Microsoft.
1 Verification of object-oriented programs with invariants Mike Barnett, Robert DeLine, Manuel Fahndrich, K. Rustan M. Leino, Wolfram Schulte ECOOP 2003.
ESOP 2010, Paphos, Cyprus, 22 March 2010 K. Rustan M. Leino (RiSE group, Microsoft Research) Peter Müller (ETH Zurich) Jan Smans (KU Leuven)
Spring 2017 Program Analysis and Verification
Specification techniques for verifying object-oriented software
Deductive Verification Tools Tutorial for Dagstuhl Seminar 16201
A Verification Infrastructure for Permission-based Reasoning
The Relationship Between Separation Logic and Implicit Dynamic Frames
The Relationship Between Separation Logic and Implicit Dynamic Frames
Methods The real power of an object-oriented programming language takes place when you start to manipulate objects. A method defines an action that allows.
Verification of concurrent object-oriented programs
A Verification Infrastructure for Permission-Based Reasoning
Hoare-style program verification
Compiler Construction
CSE 332: Concurrency and Locks
Compiler Construction
Presentation transcript:

K. Rustan M. Leino RiSE, Joint work with: Peter Müller (ETH Zurich) Jan Smans (KU Leuven) Special thanks to Mike Barnett VMCAI, Madrid, Spain, 18 January 2010

Interleaving of thread executions Unbounded number of: threads, locks, … We need some basis for doing the reasoning A way of thinking!

Experimental language with focus on: Shared-memory concurrency Static verification Key features Memory access governed by a model of permissions Sharing via locks with monitor invariants Copy-free non-blocking channels Deadlock checking, dynamic lock re-ordering Other features Classes; Mutual exclusion and readers/writers locks; Fractional permissions; Two-state monitor invariants; Asynchronous method calls; Memory leak checking; Logic predicates and functions; Ghost and prophecy variables

Access to a memory location requires permission Permissions are held by activation records Syntax for talking about permission to y: acc(y)

method Main() { var c := new Counter; call c.Inc(); } method Main() { var c := new Counter; call c.Inc(); } method Inc() requires acc(y); ensures acc(y); { y := y + 1; } method Inc() requires acc(y); ensures acc(y); { y := y + 1; } acc(c.y)

call == fork + join is semantically like … but is compiled to more efficient code call x,y := o.M(E, F); fork tk := o.M(E, F); join x,y := tk; fork tk := o.M(E, F); join x,y := tk;

class XYZ { var x: int; var y: int; var z: int; method Main() { var c := new XYZ; fork c.A(); fork c.B(); } … } class XYZ { var x: int; var y: int; var z: int; method Main() { var c := new XYZ; fork c.A(); fork c.B(); } … } method A() requires acc(x); { x := x + 1; } method A() requires acc(x); { x := x + 1; } method B() requires acc(y) && acc(z); { y := y + z; } method B() requires acc(y) && acc(z); { y := y + z; }

acc(y)write permission to y rd(y)read permission to y At any one time, at most one thread can have write permission to a location

class Fib { var x: int; var y: int; var z: int; method Main() { var c := new Fib; fork c.A(); fork c.B(); } … } class Fib { var x: int; var y: int; var z: int; method Main() { var c := new Fib; fork c.A(); fork c.B(); } … } method A() requires rd(x) && acc(y) { y := x + 21; } method A() requires rd(x) && acc(y) { y := x + 21; } method B() requires rd(x) && acc(z) { z := x + 34; } method B() requires rd(x) && acc(z) { z := x + 34; }

acc(y)100% permission to y acc(y, p)p% permission to y rd(y)read permission to y Write access requires 100% Read access requires >0% = +  acc(y)acc(y,69) acc(y,31) rd(y) acc(y,  )

What if two threads want write access to the same location? method A() … { y := y + 21; } method A() … { y := y + 21; } method B() … { y := y + 34; } method B() … { y := y + 34; } class Fib { var y: int; method Main() { var c := new Fib; fork c.A(); fork c.B(); } … } class Fib { var y: int; method Main() { var c := new Fib; fork c.A(); fork c.B(); } … } acc(c.y)

method A() … { acquire this; y := y + 21; release this; } method A() … { acquire this; y := y + 21; release this; } method B() … { acquire this; y := y + 34; release this; } method B() … { acquire this; y := y + 34; release this; } class Fib { var y: int; invariant acc(y); method Main() { var c := new Fib; share c; fork c.A(); fork c.B(); } … } class Fib { var y: int; invariant acc(y); method Main() { var c := new Fib; share c; fork c.A(); fork c.B(); } … } acc(c.y) acc(y)

The concepts holding a lock, and having permissions are orthogonal to one another In particular: Holding a lock does not imply any right to read or modify shared variables Their connection is: Acquiring a lock obtains some permissions Releasing a lock gives up some permissions

Like other specifications, monitors can hold both permissions and conditions Example: invariant acc(y) && 0 ≤ y acc(y)

[Chalice encoding by Bart Jacobs]

class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } … } class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } … }

class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } method New() returns (c: MyClass) ensures c.Valid; { … } method Mutate() requires c.Valid; ensures c.Valid; { … } } class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } method New() returns (c: MyClass) ensures c.Valid; { … } method Mutate() requires c.Valid; ensures c.Valid; { … } }

class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } method New() returns (c: MyClass) ensures c.Valid; { var c := new MyClass { x := 3, y := 5 }; fold c.Valid; } method Mutate() requires c.Valid; ensures c.Valid; { unfold c.Valid; c.y := c.y + 3; fold c.Valid; } } class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } method New() returns (c: MyClass) ensures c.Valid; { var c := new MyClass { x := 3, y := 5 }; fold c.Valid; } method Mutate() requires c.Valid; ensures c.Valid; { unfold c.Valid; c.y := c.y + 3; fold c.Valid; } } acc (c.x) acc (c.y) x ≤ y c.Validc.Valid

class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } method New() returns (c: MyClass) ensures c.Valid; { var c := new MyClass { x := 3, y := 5 }; fold c.Valid; } method Mutate() requires c.Valid; ensures c.Valid; { unfold c.Valid; c.y := c.y + 3; fold c.Valid; } } class MyClass { var x,y: int; predicate Valid { acc(c.x) && acc(c.y) && x ≤ y } method New() returns (c: MyClass) ensures c.Valid; { var c := new MyClass { x := 3, y := 5 }; fold c.Valid; } method Mutate() requires c.Valid; ensures c.Valid; { unfold c.Valid; c.y := c.y + 3; fold c.Valid; } } acc (c.x) acc (c.y) x ≤ y c.Validc.Valid c.Validc.Valid

channel Ch(c: Cell, z: int) where acc(c.y) && c.y ≤ z; channel Ch(c: Cell, z: int) where acc(c.y) && c.y ≤ z;

channel Ch(c: Cell, z: int) where acc(c.y) && c.y ≤ z; class Cell { var x,y: int; method Producer(ch: Ch) { var c := new C { x := 0, y := 0 }; send ch(c, 5); } method Consumer(ch: Ch) { receive c,z := ch; … } } channel Ch(c: Cell, z: int) where acc(c.y) && c.y ≤ z; class Cell { var x,y: int; method Producer(ch: Ch) { var c := new C { x := 0, y := 0 }; send ch(c, 5); } method Consumer(ch: Ch) { receive c,z := ch; … } } acc (c.y) acc (c.x)

channel Ch(c: Cell, z: int) where acc(c.y) && c.y ≤ z; class Cell { var x,y: int; method Producer(ch: Ch) { var c := new C { x := 0, y := 0 }; send ch(c, 5); } method Consumer(ch: Ch) { receive c,z := ch; … } } channel Ch(c: Cell, z: int) where acc(c.y) && c.y ≤ z; class Cell { var x,y: int; method Producer(ch: Ch) { var c := new C { x := 0, y := 0 }; send ch(c, 5); } method Consumer(ch: Ch) { receive c,z := ch; … } } acc (c.y) c.y ≤ z

Deadlocks are prevented by making sure no such cycle can ever occur The program partially order locks The program is checked to acquire locks in strict ascending order A deadlock is the situation where a nonempty set (cycle) of threads each waits for a resource (e.g., lock) that is held by another thread in the set

Wait order is a dense partial order (Mu, <<) with a bottom element  << is the strict version of << The wait level of an object o is stored in a mutable ghost field o.mu Accessing o.mu requires appropriate permissions, as for other fields

With these preconditions, both methods verify The conjunction of the preconditions is false, so the methods can never be invoked at the same time method M() { acquire a; acquire b; … } method M() { acquire a; acquire b; … } method N() { acquire b; acquire a; … } method N() { acquire b; acquire a; … } requires rd(a.mu); requires rd(b.mu); requires rd(a.mu) requires rd(b.mu) requires waitlevel << b.mu; requires b.mu << a.mu; requires waitlevel << a.mu; requires a.mu << b.mu;

Recall, the wait level of an object o is stored in the ghost field o.mu Initially, the.mu field is  The.mu field is set by the share statement: picks some wait level strictly between L and H, and sets o.mu to that level Provided L << H and neither denotes an extreme element, such a wait level exists, since the order is dense Changing o.mu requires acc(o.mu), as usual share o between L and H;

Given as translation to Boogie ChaliceChalice Z3Z3 BoogieBoogie Boogie is an intermediate verification language

Chalice: o.f Boogie:Heap[ o, f ] where Heap is declared to be a map from objects and field names to values To encode permissions, use another map: Mask

call M() = Exhale[[ P ]]; Inhale[[ Q ]] method M() requires P; ensures Q; method M() requires P; ensures Q;

Defined by structural induction For expression P without permission predicates Exhale P≡assert P Inhale P≡assume P Exhale acc(o.f, p) ≡ assert p ≤ Mask[o,f]; Mask[o,f] := Mask[o,f] – p; Inhale acc(o.f, p) ≡ if (Mask[o,f] == 0) { havoc Heap[o,f]; } Mask[o,f] := Mask[o,f] + p;

call M() = assert 100 ≤ Mask[this,y]; Mask[this,y] := Mask[this,y] – 100; oldH := Heap; if (Mask[this,y] = 0) { havoc Heap[this,y]; } Mask[this,y] := Mask[this,y] + 100; assume Heap[this,y] = oldH[this,y] * oldH [this,y]; class Cell { var y: int; method Square() requires acc(y); ensures acc(y) && y = old(y*y); class Cell { var y: int; method Square() requires acc(y); ensures acc(y) && y = old(y*y);

Logical constant: Let the verifier figure out K! method Square(c: Cell) requires acc(c.y); ensures acc(c.y) && c.y == old(c.y*c.y); method Square(c: Cell) requires acc(c.y); ensures acc(c.y) && c.y == old(c.y*c.y); method Square(c: Cell, ghost K: int) requires acc(c.y) && K == c.y; ensures acc(c.y) && c.y == K*K; method Square(c: Cell, ghost K: int) requires acc(c.y) && K == c.y; ensures acc(c.y) && c.y == K*K; call Square(c, c.y); call Square(c, *);

method Square(c: Cell) returns (r: int) requires acc(c.y, ε ); ensures acc(c.y, ε ) && r == c.y*c.y; method Square(c: Cell) returns (r: int) requires acc(c.y, ε ); ensures acc(c.y, ε ) && r == c.y*c.y; method Square(c: Cell) returns (r: int) requires acc(c.y); ensures acc(c.y) && r == c.y*c.y; method Square(c: Cell) returns (r: int) requires acc(c.y); ensures acc(c.y) && r == c.y*c.y; method Square(c: Cell, ghost K: perm) returns (r: int) requires acc(c.y, K); ensures acc(c.y, K) && r == c.y*c.y; method Square(c: Cell, ghost K: perm) returns (r: int) requires acc(c.y, K); ensures acc(c.y, K) && r == c.y*c.y; loses procedural abstraction ε loses procedural abstraction

A better notation? rd(c.y) ? Does that pick one K for each method activation? Can these 3 kinds of “parameters” (real, ghost, permission) be treated more uniformly? Which kinds should be indicated explicitly by the programmer and which should be figured out by the compiler? method Square(c: Cell, ghost K: perm) returns (r: int) requires acc(c.y, K); ensures acc(c.y, K) && r == c.y*c.y; method Square(c: Cell, ghost K: perm) returns (r: int) requires acc(c.y, K); ensures acc(c.y, K) && r == c.y*c.y;

Permissions guide what memory locations are allowed to be accessed Activation records can hold permissions Permissions can also be stored in various “boxes” (monitors, predicates, channels) Permissions can be transferred between activation records and boxes Locks grant mutually exclusive access to monitors

Chalice (and Boogie) available as open source: Tutorial and other papers available from: