spec# k. rustan m. leino senior researcher programming languages and methods microsoft research,...

Post on 26-Mar-2015

212 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Spec#Spec#

K. Rustan M. LeinoSenior ResearcherProgramming Languages and MethodsMicrosoft Research, Redmond, WA, USA

K. Rustan M. LeinoSenior ResearcherProgramming Languages and MethodsMicrosoft Research, Redmond, WA, USA

Microsoft Research faculty summit, Redmond, WA, USA, 17 July 2006

joint work withMike Barnett, Robert DeLine, Manuel Fähndrich, Wolfram Schulte, Herman Venter,

and internsBor-Yuh Evan Chang, Ádám Darvas, Bart Jacobs, Daan Leijen, Angela Wallenburg,

and visiting researchersFrancesco Logozzo, Peter Müller, David A. Naumann, Arnd Poetzsch-Heffter

Software engineering problemSoftware engineering problem

Building and maintaining large systems that are correctBuilding and maintaining large systems that are correct

ApproachApproach

Specifications record design decisionsbridge intent and code

Tools amplify human effortmanage details

find inconsistencies

ensure quality

Specifications record design decisionsbridge intent and code

Tools amplify human effortmanage details

find inconsistencies

ensure quality

Research goalsResearch goals

Build the best such system we can build today

Experiment with the system to get a feel for what it is like to use

Advance the state of the art

Build the best such system we can build today

Experiment with the system to get a feel for what it is like to use

Advance the state of the art

Spec#Spec#Experimental mix of contracts and tool support

Aimed at experienced developers who know the high cost of testing and maintenance

Superset of C#non-null types

pre- and postconditions

object invariants

Tool supportmore type checking

compiler-emitted run-time checks

static program verification

Experimental mix of contracts and tool support

Aimed at experienced developers who know the high cost of testing and maintenance

Superset of C#non-null types

pre- and postconditions

object invariants

Tool supportmore type checking

compiler-emitted run-time checks

static program verification

C#contracts

everywhere

type checking

static verification

into the future

run-time checks

degree of checking,effort

familiar

Spec# demo: ChunkerSpec# demo: Chunker

Some design issuesSome design issues

0. Non-null types

1. C# compatibility

2. Preconditions

3. Object invariants

4. Program verifier architecture

5. Verification-condition generation

0. Non-null types

1. C# compatibility

2. Preconditions

3. Object invariants

4. Program verifier architecture

5. Verification-condition generation

T x; The value of x is null ora reference to an object whose type is a subtype of T.

T ! y; The value of y isa reference to an object whose type is a subtype of T,not null.

T x; The value of x is null ora reference to an object whose type is a subtype of T.

T ! y; The value of y isa reference to an object whose type is a subtype of T,not null.

0. Non-null types0. Non-null types

Non-null escape hatch: castNon-null escape hatch: cast

object o;string s;

string! a = (string!)o;

string! b = (!)s;

object o;string s;

string! a = (string!)o;

string! b = (!)s;

Comparing against nullComparing against null

public void M( T x ) {

if (x == null) {…

} else {int y = ((!)x).f;…

}}

public void M( T x ) {

if (x == null) {…

} else {int y = ((!)x).f;…

}}

Comparing against nullComparing against null

public void M( T x ) {

if (x == null) {…

} else {int y = x.f;…

}}

public void M( T x ) {

if (x == null) {…

} else {int y = x.f;…

}} Spec# performs a data-flow analysis to

allow this (similar to definite assignment)

Non-null instance fieldsNon-null instance fields

class C : B {T ! x;public C(T ! y): base(){

this.x = y;}public override int M() { return x.f; }

}

class C : B {T ! x;public C(T ! y): base(){

this.x = y;}public override int M() { return x.f; }

}

Is this code type safe?No!

abstract class B {public B() { this.M(); }

public abstract int M();

}

null dereferencenull dereference

Non-null instance fieldsNon-null instance fields

class C : B {T ! x;public C(T ! y){

this.x = y;base();

}public override int M() { return x.f; }

}

class C : B {T ! x;public C(T ! y){

this.x = y;base();

}public override int M() { return x.f; }

}

Spec# allows x to beassigned before baseconstructor is called.

Other non-null issuesOther non-null issues

Comparing a field against nullif (this.f != null) {

// …this.f.M(…);

}

Static fieldsstatic T g = new T();

ArraysT![ ] a = new T![100];

GenericsList<T!> myList = new List<T!>();

Comparing a field against nullif (this.f != null) {

// …this.f.M(…);

}

Static fieldsstatic T g = new T();

ArraysT![ ] a = new T![100];

GenericsList<T!> myList = new List<T!>();

Spec# is superset of C#

From C# to Spec#:accept every C# program

compile it to have the same behavior

Consequences“Possible null dereference” is just a warning

“Must initialize non-null fields before calling base constructor” is an error

Support for out-of-band contracts

Spec# is superset of C#

From C# to Spec#:accept every C# program

compile it to have the same behavior

Consequences“Possible null dereference” is just a warning

“Must initialize non-null fields before calling base constructor” is an error

Support for out-of-band contracts

1. C# compatibility1. C# compatibility

From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005

class B : A {string! src;public B(string! source, int x)

requires 0 <= x;{

this.src = source;base(x);

}

class B : A {string! src;public B(string! source, int x)

requires 0 <= x;{

this.src = source;base(x);

}

From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005

class B : A {string! src;public B(string! source, int x)

//^ requires 0 <= x;{

this.src = source;base(x);

}

class B : A {string! src;public B(string! source, int x)

//^ requires 0 <= x;{

this.src = source;base(x);

}

From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005

class B : A {string/*!*/ src;public B(string/*!*/ source, int x)

//^ requires 0 <= x;{

this.src = source;base(x);

}

class B : A {string/*!*/ src;public B(string/*!*/ source, int x)

//^ requires 0 <= x;{

this.src = source;base(x);

}

From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005From Spec# to C#or: Leveraging wiz-bang features of Visual Studio 2005

class B : A {string/*!*/ src;public B(string/*!*/ source, int x)

//^ requires 0 <= x; : base(x){

this.src = source;//^ base;

}

class B : A {string/*!*/ src;public B(string/*!*/ source, int x)

//^ requires 0 <= x; : base(x){

this.src = source;//^ base;

}

2. Preconditions2. Preconditions

StringBuilder.Append Method (Char[ ], Int32, Int32)Appends the string representation of a specified subarray of Unicode characters to the end of this instance.

public StringBuilder Append(char[] value, int startIndex, int charCount);

Parameters

valueA character array.

startIndexThe starting position in value.

charCountThe number of characters append.

Return Value

A reference to this instance after the append operation has occurred.

Exceptions

Contracts todayContracts today

Exception Type Condition

ArgumentNullException value is a null reference, and startIndex and charCount are not zero.

ArgumentOutOfRangeException charCount is less than zero.

-or-

startIndex is less than zero.

-or-

startIndex + charCount is less than the length of value.

Contract in Spec#Contract in Spec#

public StringBuilder Append(char[ ] value, int startIndex,int charCount );

requires value == null ==> startIndex == 0 && charCount == 0;

requires 0 <= startIndex;

requires 0 <= charCount;

requires value == null || startIndex + charCount <= value.Length;

public StringBuilder Append(char[ ] value, int startIndex,int charCount );

requires value == null ==> startIndex == 0 && charCount == 0;

requires 0 <= startIndex;

requires 0 <= charCount;

requires value == null || startIndex + charCount <= value.Length;

Exception Type Condition

ArgumentNullException value is a null reference, and startIndex and charCount are not zero.

ArgumentOutOfRangeException charCount is less than zero.

-or-

startIndex is less than zero.

-or-

startIndex + charCount is less than the length of value.

Otherwise clausesOtherwise clauses

public StringBuilder Append(char[ ] value, int startIndex,int charCount );

requires value == null ==> startIndex == 0 && charCount == 0otherwise ArgumentNullException;

requires 0 <= startIndexotherwise ArgumentOutOfRangeException;

public StringBuilder Append(char[ ] value, int startIndex,int charCount );

requires value == null ==> startIndex == 0 && charCount == 0otherwise ArgumentNullException;

requires 0 <= startIndexotherwise ArgumentOutOfRangeException;

Exception Type Condition

ArgumentNullException value is a null reference, and startIndex and charCount are not zero.

ArgumentOutOfRangeException charCount is less than zero.

-or-

startIndex is less than zero.

-or-

startIndex + charCount is less than the length of value.

Inheriting contractsInheriting contracts

interface J {void M(int x); requires P;

}

class A {public abstract void M(int x); requires Q;

}

class B : A, J {public override void M(int x){ … }

}

interface J {void M(int x); requires P;

}

class A {public abstract void M(int x); requires Q;

}

class B : A, J {public override void M(int x){ … }

}

3. Object invariants3. Object invariants

When do object invariants hold?When do object invariants hold?

class C {

private int x;private int y;

invariant x < y;

public C() { x = 0; y = 1; }

public void M(){

int t = 100 / (y – x);x = x + 1;P(t);y = y + 1;

}

}

class C {

private int x;private int y;

invariant x < y;

public C() { x = 0; y = 1; }

public void M(){

int t = 100 / (y – x);x = x + 1;P(t);y = y + 1;

}

}

invariant assumed to holdon entry to method

invariant checked to holdon exit from method

invariant checked to holdat end of constructor

invariant may betemporarily broken here

invariant is restored here

what if P calls back into M?

Object statesObject states

MutableObject invariant may not hold

Field updates allowed

ValidObject invariant holds

Field updates not allowed

MutableObject invariant may not hold

Field updates allowed

ValidObject invariant holds

Field updates not allowed

Valid vs. mutable objectsValid vs. mutable objectsclass C {

private int x;private int y;invariant x < y;

public void M()requires this.inv == Valid;

{expose (this) {

int t = 100 / (y – x);x = x + 1;P(t);y = y + 1;

}}…

}

class C {private int x;private int y;invariant x < y;

public void M()requires this.inv == Valid;

{expose (this) {

int t = 100 / (y – x);x = x + 1;P(t);y = y + 1;

}}…

}

represent explicitlythat invariant holds(without revealing

what the invariant is)

change this.invfrom Valid to Mutable

check invariant;then, change this.invfrom Mutable to Valid

field updates allowedonly on Mutable objects

Summary of object invariantsSummary of object invariants

invariant …

inv : { Mutable, Valid }

expose

updates of o.f require o.inv = Mutable

(o ・ o.inv = Mutable Inv (o))

invariant …

inv : { Mutable, Valid }

expose

updates of o.f require o.inv = Mutable

(o ・ o.inv = Mutable Inv (o))

4. Spec# verifier architecture4. Spec# verifier architecture

V.C. generator

automatictheorem prover

verification condition

Spec#

“correct” or list of errors

Spec# compiler

MSIL (“bytecode”)

bytecode translator

Boogie PL

inference engine

Spec# program verifier (aka Boogie)

BoogiePLBoogiePL

Intermediate languagetheory part

imperative part

Semantics of Spec# is encoded in BoogiePL

Can be used for other program-verification tasks, like verifying other source languages

Intermediate languagetheory part

imperative part

Semantics of Spec# is encoded in BoogiePL

Can be used for other program-verification tasks, like verifying other source languages

Example BoogiePL (0)Example BoogiePL (0)var $Heap: [ref,name]any where IsHeap($Heap);function IsHeap(h: [ref,name]any) returns (bool);

const Chunker: name;axiom Chunker <: System.Object;

const Chunker.n: name;function DeclType(field: name) returns (class: name);axiom DeclType(Chunker.n) = Chunker;

const $allocated: name;axiom ( h: [ref,name]any, o: ref, f: name •

IsHeap(h) h[o, $allocated] h[h[o, f],$allocated]);

const $inv: name;axiom ( $oi: ref, $h: [ref,name]any •

IsHeap($h) $h[$oi, $inv] <: Chunker0 < $h[$oi, Chunker.ChunkSize] 0 ≤ $h[$oi, Chunker.n] $h[$oi, Chunker.n] ≤ $Length($h[$oi, Chunker.src]));

var $Heap: [ref,name]any where IsHeap($Heap);function IsHeap(h: [ref,name]any) returns (bool);

const Chunker: name;axiom Chunker <: System.Object;

const Chunker.n: name;function DeclType(field: name) returns (class: name);axiom DeclType(Chunker.n) = Chunker;

const $allocated: name;axiom ( h: [ref,name]any, o: ref, f: name •

IsHeap(h) h[o, $allocated] h[h[o, f],$allocated]);

const $inv: name;axiom ( $oi: ref, $h: [ref,name]any •

IsHeap($h) $h[$oi, $inv] <: Chunker0 < $h[$oi, Chunker.ChunkSize] 0 ≤ $h[$oi, Chunker.n] $h[$oi, Chunker.n] ≤ $Length($h[$oi, Chunker.src]));

Example BoogiePL (1)Example BoogiePL (1)procedure Chunker.NextChunk(this: ref) returns ($result: ref); requires $Heap[this, $inv] = Chunker; requires $Heap[this, $ownerFrame] = $PeerGroupPlaceholder

¬($Heap[$Heap[this, $ownerRef], $inv] <: $Heap[this, $ownerFrame]); free requires $Heap[this, $allocated] = true $IsNotNull(this, Chunker); modifies $Heap; ensures $Length($result) ≤ $Heap[this, Chunker.ChunkSize]; ensures ( $pc: ref • $pc ≠ null $Heap[$pc, $allocated] = true

$Heap[$pc, $ownerRef] = $Heap[$result, $ownerRef] $Heap[$pc, $ownerFrame] = $Heap[$result, $ownerFrame]($Heap[$pc, $ownerFrame] = $PeerGroupPlaceholder

¬($Heap[$Heap[$pc, $ownerRef], $inv] <: $Heap[$pc, $ownerFrame])) $Heap[$pc, $inv] = $typeof($pc));

free ensures $Heap[$result, $allocated] = true $IsNotNull($result, System.String); free ensures ( $o: ref • $o ≠ null ¬old($Heap)[$o, $allocated] $Heap[$o, $allocated]

$Heap[$o, $inv] = $typeof($o)); ensures ( $o: ref • $o ≠ null old($Heap)[$o, $allocated] = true

old($Heap)[$Heap[$o, $ownerRef], $allocated] = trueold($Heap)[$o, $ownerRef] = $Heap[$o, $ownerRef] old($Heap)[$o, $ownerFrame] = $Heap[$o, $ownerFrame]);

free ensures ( $o: ref, $f: name • $f ≠ $inv $o ≠ null old($Heap)[$o, $allocated] = true (old($Heap)[$o, $ownerFrame] = $PeerGroupPlaceholder

¬(old($Heap)[old($Heap)[$o, $ownerRef], $inv] <: old($Heap)[$o, $ownerFrame])) (¬IsStaticField($f) ¬IsDirectlyModifiableField($f)) old($Heap)[$o, $f] = $Heap[$o, $f]);

free ensures ( $o: ref • old($Heap)[$o, $inv] = $Heap[$o, $inv] old($Heap)[$o, $allocated] ≠ true);

free ensures ( $o: ref • old($Heap)[$o, $allocated] $Heap[$o, $allocated]);

procedure Chunker.NextChunk(this: ref) returns ($result: ref); requires $Heap[this, $inv] = Chunker; requires $Heap[this, $ownerFrame] = $PeerGroupPlaceholder

¬($Heap[$Heap[this, $ownerRef], $inv] <: $Heap[this, $ownerFrame]); free requires $Heap[this, $allocated] = true $IsNotNull(this, Chunker); modifies $Heap; ensures $Length($result) ≤ $Heap[this, Chunker.ChunkSize]; ensures ( $pc: ref • $pc ≠ null $Heap[$pc, $allocated] = true

$Heap[$pc, $ownerRef] = $Heap[$result, $ownerRef] $Heap[$pc, $ownerFrame] = $Heap[$result, $ownerFrame]($Heap[$pc, $ownerFrame] = $PeerGroupPlaceholder

¬($Heap[$Heap[$pc, $ownerRef], $inv] <: $Heap[$pc, $ownerFrame])) $Heap[$pc, $inv] = $typeof($pc));

free ensures $Heap[$result, $allocated] = true $IsNotNull($result, System.String); free ensures ( $o: ref • $o ≠ null ¬old($Heap)[$o, $allocated] $Heap[$o, $allocated]

$Heap[$o, $inv] = $typeof($o)); ensures ( $o: ref • $o ≠ null old($Heap)[$o, $allocated] = true

old($Heap)[$Heap[$o, $ownerRef], $allocated] = trueold($Heap)[$o, $ownerRef] = $Heap[$o, $ownerRef] old($Heap)[$o, $ownerFrame] = $Heap[$o, $ownerFrame]);

free ensures ( $o: ref, $f: name • $f ≠ $inv $o ≠ null old($Heap)[$o, $allocated] = true (old($Heap)[$o, $ownerFrame] = $PeerGroupPlaceholder

¬(old($Heap)[old($Heap)[$o, $ownerRef], $inv] <: old($Heap)[$o, $ownerFrame])) (¬IsStaticField($f) ¬IsDirectlyModifiableField($f)) old($Heap)[$o, $f] = $Heap[$o, $f]);

free ensures ( $o: ref • old($Heap)[$o, $inv] = $Heap[$o, $inv] old($Heap)[$o, $allocated] ≠ true);

free ensures ( $o: ref • old($Heap)[$o, $allocated] $Heap[$o, $allocated]);

Example BoogiePL (2)Example BoogiePL (2)implementation Chunker.NextChunk(this: ref) returns ($result: ref){ var temp0: ref, local4: ref, stack0i: int, stack1i: int, stack1o: ref,

stack0b: bool, stack0o: ref, stack2i: int, s: ref, return.value: ref,SS$Display.Return.Local: ref;

entry: // … // ----- load field ----- Chunker.ssc(14,7) assert this ≠ null; stack1o := $Heap[this, Chunker.src]; // … // ----- binary operator ----- Chunker.ssc(14,7) stack0b := stack0i > stack1i; // ----- branch ----- Chunker.ssc(14,7) goto true5814to5848, false5814to5831;

true5814to5848: assume stack0b = true; …

implementation Chunker.NextChunk(this: ref) returns ($result: ref){ var temp0: ref, local4: ref, stack0i: int, stack1i: int, stack1o: ref,

stack0b: bool, stack0o: ref, stack2i: int, s: ref, return.value: ref,SS$Display.Return.Local: ref;

entry: // … // ----- load field ----- Chunker.ssc(14,7) assert this ≠ null; stack1o := $Heap[this, Chunker.src]; // … // ----- binary operator ----- Chunker.ssc(14,7) stack0b := stack0i > stack1i; // ----- branch ----- Chunker.ssc(14,7) goto true5814to5848, false5814to5831;

true5814to5848: assume stack0b = true; …

Example BoogiePL (3)Example BoogiePL (3)

// ----- call ----- Chunker.ssc(17,9) assert stack0o ≠ null; call s := System.String.Substring$System.Int32(stack0o, stack1i); // … // ----- store field ----- Chunker.ssc(19,7) assert this ≠ null; assert ¬($Heap[this, $inv] <: Chunker); $Heap[this, Chunker.n] := stack0i; // … // ----- return $result := stack0o; return;}

// ----- call ----- Chunker.ssc(17,9) assert stack0o ≠ null; call s := System.String.Substring$System.Int32(stack0o, stack1i); // … // ----- store field ----- Chunker.ssc(19,7) assert this ≠ null; assert ¬($Heap[this, $inv] <: Chunker); $Heap[this, Chunker.n] := stack0i; // … // ----- return $result := stack0o; return;}

5. Verification conditions5. Verification conditions

Automatic theorem provercan be hidden from programmer

generates counterexamples

Interactive theorem proverrequires gurus

not limited by built-in decision procedures

Automatic theorem provercan be hidden from programmer

generates counterexamples

Interactive theorem proverrequires gurus

not limited by built-in decision procedures

Performance considerationsPerformance considerations

Generate verification conditions that the theorem prover can handle quickly

“Efficient” encodings of axioms

“Efficient” weakest preconditions

Generate verification conditions that the theorem prover can handle quickly

“Efficient” encodings of axioms

“Efficient” weakest preconditions

Initial verifier experienceInitial verifier experience

Pilot production project“it changes how you think”

Several smaller (300-1500 lines) case studies

Parts of Spec# program verifier

External academic use

Pilot production project“it changes how you think”

Several smaller (300-1500 lines) case studies

Parts of Spec# program verifier

External academic use

download Spec#from here

ConclusionsConclusionsBecause of tool support, we’re ready for programming at the next level of rigorCurrent work

Specification/programming/verification methodologyPerformanceTechnology transferEngineering effort

Technology sharingTeachingCase studiesBoogiePL as common intermediate logic

Because of tool support, we’re ready for programming at the next level of rigorCurrent work

Specification/programming/verification methodologyPerformanceTechnology transferEngineering effort

Technology sharingTeachingCase studiesBoogiePL as common intermediate logic

http://research.microsoft.com/~leino

http://research.microsoft.com/specsharp

top related