Fractional Permissions without the Fractions Alex Summers ETH Zurich Joint work with: Stefan Heule, Rustan Leino, Peter Müller ETH Zurich MSR Redmond ETH.

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,
Technologies for finding errors in object-oriented software K. Rustan M. Leino Microsoft Research, Redmond, WA Lecture 3 Summer school on Formal Models.
Technologies for finding errors in object-oriented software K. Rustan M. Leino Microsoft Research, Redmond, WA Lecture 1 Summer school on Formal Models.
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.
Substitution & Evaluation Order cos 441 David Walker.
Synthesis, Analysis, and Verification Lecture 04c Lectures: Viktor Kuncak VC Generation for Programs with Data Structures “Beyond Integers”
Abstraction and Modular Reasoning for the Verification of Software Corina Pasareanu NASA Ames Research Center.
Constraint Semantics for Abstract Read Permissions 28 th July 2014, FTfJP, Uppsala John Tang Boyland (UW-Milwaukee/ETH Zurich) Peter Müller, Malte Schwerhoff,
Automated Verification with HIP and SLEEK Asankhaya Sharma.
Automated Software Verification with a Permission-Based Logic 20 th June 2014, Zürich Malte Schwerhoff, ETH Zürich.
Goldilocks: Efficiently Computing the Happens-Before Relation Using Locksets Tayfun Elmas 1, Shaz Qadeer 2, Serdar Tasiran 1 1 Koç University, İstanbul,
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.
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond, WA, USA 3 December 2008 U. Lugano Lugano, Switzerland.
ISBN Chapter 3 Describing Syntax and Semantics.
Changing perspective can be useful Relating alternative logics for automatic software verification Alex Summers (ETH Zurich) partly based on joint work.
ECI 2007: Specification and Verification of Object-Oriented Programs Lecture 2 Courtesy: K. Rustan M. Leino and Wolfram Schulte.
C. FlanaganSAS’04: Type Inference Against Races1 Type Inference Against Races Cormac Flanagan UC Santa Cruz Stephen N. Freund Williams College.
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.
Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 18 Program Correctness To treat programming.
CS 330 Programming Languages 09 / 18 / 2007 Instructor: Michael Eckmann.
Houdini: An Annotation Assistant for ESC/Java Cormac Flanagan and K. Rustan M. Leino Compaq Systems Research Center.
Copyright © 2006 The McGraw-Hill Companies, Inc. Programming Languages 2nd edition Tucker and Noonan Chapter 18 Program Correctness To treat programming.
CS 61C L4 Structs (1) A Carle, Summer 2005 © UCB inst.eecs.berkeley.edu/~cs61c/su05 CS61C : Machine Structures Lecture #4: Strings & Structs
ESC Java. Static Analysis Spectrum Power Cost Type checking Data-flow analysis Model checking Program verification AutomatedManual ESC.
1 A Modular Checker for Multithreaded Programs Cormac Flanagan HP Systems Research Center Joint work with Shaz Qadeer Sanjit A. Seshia.
Describing Syntax and Semantics
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond Caltech Pasadena, CA 12 November 2009.
Subclasses and Subtypes CMPS Subclasses and Subtypes A class is a subclass if it has been built using inheritance. ▫ It says nothing about the meaning.
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.
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.
1 Abstraction  Identify important aspects and ignore the details  Permeates software development programming languages are abstractions built on hardware.
Aquinas Hobor and Cristian Gherghina (National University of Singapore) TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.:
Viper A Verification Infrastructure for Permission-Based Reasoning 1 st March 2015, ECOOP’15 PC Meeting, Zurich Uri Juhasz, Ioannis Kassios, Peter Müller,
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.
A Universe-Type-Based Verification Technique for Mutable Static Fields and Methods Alexander J Summers Sophia Drossopoulou Imperial College London Peter.
Looping and Counting Lecture 3 Hartmut Kaiser
ICS 313: Programming Language Theory Chapter 13: Concurrency.
CS533 – Spring Jeanie M. Schwenk Experiences and Processes and Monitors with Mesa What is Mesa? “Mesa is a strongly typed, block structured programming.
Lightweight Support for Magic Wands in an Automatic Verifier Malte Schwerhoff and Alexander J. Summers 10 th July 2015, ECOOP, Prague.
K. Rustan M. Leino Research in Software Engineering (RiSE) Microsoft Research, Redmond, WA part 2 International Summer School Marktoberdorf Marktoberdorf,
Year 9 Proof Dr J Frost Last modified: 19 th February 2015 Objectives: Understand what is meant by a proof, and examples.
1 Assertions. 2 A boolean expression or predicate that evaluates to true or false in every state In a program they express constraints on the state that.
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.
Extended Static Checking for Java Cormac Flanagan Joint work with: Rustan Leino, Mark Lillibridge, Greg Nelson, Jim Saxe, and Raymie Stata Compaq Systems.
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)
Faithful mapping of model classes to mathematical structures Ádám Darvas ETH Zürich Switzerland Peter Müller Microsoft Research Redmond, WA, USA SAVCBS.
VPERM: Variable Permissions for Concurrency Verification Duy-Khanh Le, Wei-Ngan Chin, Yong-Meng Teo ICFEM, Kyoto, Japan, Nov 2012.
NIRICT RECONNAISSANCE TOPIC PERFORMANCE AND CORRECTNESS OF GPGPU MARIEKE HUISMAN ALEXANDRU IOSUP ANA LUCIA VARBANESCU ANTON WIJS.
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
Modular Verification of Message Passing Programs
Verification of concurrent object-oriented programs
Programming Languages 2nd edition Tucker and Noonan
Over-Approximating Boolean Programs with Unbounded Thread Creation
A Verification Infrastructure for Permission-Based Reasoning
Programming Languages 2nd edition Tucker and Noonan
Programming Languages 2nd edition Tucker and Noonan
A Considerate Specification of the Composite Pattern
Presentation transcript:

Fractional Permissions without the Fractions Alex Summers ETH Zurich Joint work with: Stefan Heule, Rustan Leino, Peter Müller ETH Zurich MSR Redmond ETH Zurich

Overview Verification of (race-free) concurrent programs, using (something like) fractional permissions Background Problem: picking rational values Abstract read permissions Handling calls, fork/join, monitors Permission expressions Conclusions

Fractional Permissions (Boyland) Provide a way of describing disciplined (race- free) use of shared memory locations. Many readers ✓ One writer ✓ Not both. Heap locations are managed using permissions ▫passed between threads, but never duplicated Permission amounts are rationals p from [0,1] ▫p=0 (no permission) ▫0<p<1 (read permission) ▫p=1 (read/write permission) Permissions can be split and recombined

Implicit Dynamic Frames (Smans) Uses permissions as assertions to control which threads can read/write to heap locations Permissions can be fractional Extend first-order logic assertions to additionally include “accessibility predicates”: acc(x.f, p) ; we have permission p to location x.f For example, acc(x.f,1) && x.f == 4 && acc(x.g,1) Permissions treated multiplicatively; i.e., ▫acc(x.f,p) && acc(x.f,p) ≡ acc(x.f,2p) Related to Sep. Logic * [Parkinson/Summers’11]

Chalice (Leino & Müller) Verification tool for concurrent programs ▫race-freedom, deadlock-freedom, functional specs Specification logic : Implicit Dynamic Frames Supports weak fractional permissions ▫acc(e.f, n%) – integer percentages (0<n ≤ 100) Also counting permissions (not discussed here) Verification conditions are generated in terms of ▫Heap variable – tracks information about heap ▫Mask variable – tracks permissions currently held Modular verification – per method declaration.

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Inhale and Exhale “inhale P” and “exhale P” are used in Chalice to encode transfers between threads/calls “inhale P” means: ▫assume heap properties in p ▫gain permissions in p ▫havoc newly-readable locations “exhale P” means: ▫assert heap properties in p ▫check and give up permissions void m() requires p ensures q { // inhale P... // exhale P call m() // inhale Q... // exhale Q }

Problem / Aims We always need to specify fractional (read) permissions using precise (rational) values. ▫Manual book-keeping is tedious ▫Creates ‘noise’ in specifications, and limits re-use ▫User only cares about read or write permissions Aim: abstract over concrete permission amounts ▫User doesn’t choose amounts for read permissions Want decent performance from theorem provers Also, unbounded splitting of permissions...

Permission splitting class Node { Node l,r; Outcome work(Data d) requires «permission to d.f»; ensures «permission to d.f»; { if (l != null) fork outL := l.work(d); if (r != null) fork outR := r.work(d); Outcome out := /* work on this node, using d.f */ if (l != null) out.combine(join outL); if (r != null) out.combine(join outR); return out; } How much permission?

Idea: abstract read permissions Introduce new read permissions: acc(e.f, rd) ▫Represents an (a priori unknown), positive fractional permission ▫Positive amount: allows reading of location e.f Fractions are never expressed precisely ▫We generate (satisfiable) constraints on them ▫Specifications written using just:  read permissions: acc(e.f, rd) or simply rd(e.f)  write permissions: acc(e.f, 100%) or simply acc(e.f) ▫Different read permissions can refer to different amounts. But, sometimes we want them to match..

Matching rd permissions For example, method calls often take some permission and then return it to the caller: Rule: for a given method call, every rd permission in a method specification is interpreted by the same permission amount method m(c: Cell) requires rd(c.val); ensures rd(c.val); { /* do something fun... */ } method main(c: Cell) requires acc(c.val); { c.value := 0; call m(c); c.value := 1; }

A recursive method... method m(c: Cell) requires rd(c.val); ensures rd(c.val); { // do stuff call m(c); // do stuff } Declare fraction f m ; used to interpret rd in current method specification: 0 < f m ≤ 1 Exhale precondition for recursive call Declare 0 < f call ≤ 1 ( rd amounts in recursive call) Check that we have some permission amount assert Mask[c.val] > 0 Constrain f call to be smaller than permission we have assume f call < Mask[c.val] Give away this amount: Mask[c.val] -= f call Inhale postcondition: Mask[c.val] += f call Inhale precondition Mask[c.val] += f m Exhale postcondition Check available permission assert Mask[c.val] >= f m Remove permission from mask Mask[c.val] -= f m

Losing permission What if we don’t intend to return same amount? Introduce rd* method m(c: Cell) requires rd(c.val); ensures rd(c.val); { fork tk := m(c); } method m(c: Cell) requires rd(c.val); ensures rd*(c.val); { fork tk := m(c); } exhale post-condition: Check available permission assert Mask[c.val] >= f m ✘ exhale post-condition: Check some available permission assert Mask[c.val] > 0 ✓ Unknown amount returned to caller represents a different (positive) fraction – with no other information

Monitors Locks are associated with monitor invariants ▫inhale monitor invariant on acquire of lock ▫exhale monitor invariant on release of lock How should read permission in monitor invariants be interpreted? Recall: for methods, we “choose” a value that is convenient at each call site. Can we do the same when we transfer read permission into a monitor?

Monitors Analogous idea: fix fraction at release /*... */ release lock; /*... */ acquire lock; /*... */ Thread 1 class Lock { var x: int; invariant rd(x); }

Monitors Analogous idea: fix fraction at release Fraction needs to be fixed at object creation ▫Not possible at share for similar reasons /*... */ release lock; /*... */ acquire lock; /*... */ Thread 1 acquire lock; /*... */ release lock; Thread 2 class Lock { var x: int; invariant rd(x); }

Monitors method main(lock: Lock) requires rd(x); { release lock; } class Lock { var x: int; invariant rd(x); }

Monitors Solution 1: Use rd*(x) in monitor No guarantee that permission we get back is the same, when we re-acquire monitor method main(lock: Lock) requires rd(x); { release lock; acquire lock; } Only need to check that we have some permission. class Lock { var x: int; invariant rd*(x); }

Example Revisited class Node { Node l,r; Outcome work(Data d) requires «permission to d.f»; ensures «permission to d.f»; { if (l != null) fork outL := l.work(d); if (r != null) fork outR := r.work(d); Outcome out := /* work on this node, using d.f */ if (l != null) out.combine(join outL); if (r != null) out.combine(join outR); return out; }

Example Revisited rd permissions sufficient to specify the example class Node { Node l,r; Outcome work(Data d) requires rd(d.f); ensures rd(d.f); { if (l != null) fork outL := l.work(d); if (r != null) fork outR := r.work(d); Outcome out := /* work on this node, using d.f */ if (l != null) out.combine(join outL); if (r != null) out.combine(join outR); return out; } Some amount(s) given away, but not all Same amount(s) are retrieved

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work out1 := call w.ask(task1, d); out2 := call w.ask(task2, d); //... drink coffee join out1; join out2; d.f := // modify data } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } } How do we know we get back all the permissions we gave away? Intuitively, ask returns the permission it was passed minus the permission held by the forked thread do requires rd access to the shared data ask requires rd access to the shared data, and gives some of this permission to the newly-forked thread

Permission expressions We need a way to express (unknown) amounts of read permission held by a forked thread We also need to be able to express the difference between two permission amounts We generalise our permissions: acc(e.f, P) ▫ where P is a permission expression:  100% or rd (as before)  rd(tk) where tk is a token for a forked thread  rd(m) where m is a monitor  P 1 + P 2 or P 1 - P 2 Easy to encode, and is much more expressive...

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work out1 := call w.ask(task1, d); out2 := call w.ask(task2, d); //... drink coffee join out1; join out2; d.f := // modify data } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } requires acc(d.f, rd) ensures acc(d.f, rd – rd(result)) requires acc(d.f, rd) ensures acc(d.f, rd) requires acc(d.f, 100%) ensures acc(d.f, 100%)

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work // 100% out1 := call w.ask(task1, d); out2 := call w.ask(task2, d); //... drink coffee join out1; join out2; d.f := // modify data } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } requires acc(d.f, rd) ensures acc(d.f, rd – rd(result)) requires acc(d.f, 100%) ensures acc(d.f, 100%) requires acc(d.f, rd) ensures acc(d.f, rd)

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work // 100% out1 := call w.ask(task1, d); // 100% - rd(out1) out2 := call w.ask(task2, d); //... drink coffee join out1; join out2; d.f := // modify data } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } requires acc(d.f, rd) ensures acc(d.f, rd – rd(result)) requires acc(d.f, 100%) ensures acc(d.f, 100%) requires acc(d.f, rd) ensures acc(d.f, rd)

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work // 100% out1 := call w.ask(task1, d); // 100% - rd(out1) out2 := call w.ask(task2, d); // 100% - rd(out1) – rd(out2) //... drink coffee join out1; join out2; d.f := // modify data } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } requires acc(d.f, rd) ensures acc(d.f, rd – rd(result)) requires acc(d.f, 100%) ensures acc(d.f, 100%) requires acc(d.f, rd) ensures acc(d.f, rd)

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work // 100% out1 := call w.ask(task1, d); // 100% - rd(out1) out2 := call w.ask(task2, d); // 100% - rd(out1) – rd(out2) //... drink coffee join out1; join out2; // 100% d.f := // modify data } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } requires acc(d.f, rd) ensures acc(d.f, rd – rd(result)) requires acc(d.f, 100%) ensures acc(d.f, 100%) requires acc(d.f, rd) ensures acc(d.f, rd)

class Management { Data d; // shared data... void manage(Workers w) { //... make up some work // 100% out1 := call w.ask(task1, d); // 100% - rd(out1) out2 := call w.ask(task2, d); // 100% - rd(out1) – rd(out2) //... drink coffee join out1; join out2; // 100% d.f := // modify data // ✓ can write } class Workers { Outcome do(Task t, Data d, Plan p) {... } token ask(Task t, Data d) { fork out := call do(t,d,plan); return out; } requires acc(d.f, rd) ensures acc(d.f, rd – rd(result)) requires acc(d.f, 100%) ensures acc(d.f, 100%) requires acc(d.f, rd) ensures acc(d.f, rd)

Monitors Recall the awkward situation with monitors: method main(Lock: lock) requires rd(x); { release lock; acquire lock; } class Lock { int x; invariant rd(x); }

Monitors Solution 2: Using the permission expressions Now we can express exactly the amount of permission we need to exhale to the monitor. method main(Lock lock) requires acc(x,rd(lock)); { release lock; acquire lock; } class Lock { int x; invariant rd(x); }

Summary and Extras Presented a specification methodology: ▫similar expressiveness to fractional permissions ▫avoids explicit “values” for read permissions ▫allows user to reason about read/write abstractly Supports full Chalice language ▫fork/join, channels, predicates, loop invariants Methodology is implemented ▫backwards-compatible with a few easy edits ▫permission encoding uses only integer-typed data ▫performance comparable with existing encoding

Future Work We cannot express the permission left over after we fork off an unbounded number of threads ▫mathematical sums in permission expressions ▫e.g., acc(x, 100% – Σ i rd(tk i )) ▫some careful encoding is required to perform well In some obscure cases, permission multiplication arises ▫non-linear arithmetic tends to perform badly Experiment with encoding harder fractional examples using abstract permission expressions

Are there any questions?