Formal reasoning about runtime code update Billiejoe (Nathaniel) Charlton Ben Horsfall Bernhard Reus...

Post on 16-Dec-2015

212 views 0 download

Transcript of Formal reasoning about runtime code update Billiejoe (Nathaniel) Charlton Ben Horsfall Bernhard Reus...

Formal reasoning about runtime code update

Billiejoe (Nathaniel) Charlton

Ben Horsfall

Bernhard Reus

HotSWUp 2011

Outline

• Discuss how to do formal proofs about safety of runtime code updates

- Using a (relatively) new variant of Hoare logic

- specifically, Hoare logic with nested Hoare triples

Outline

• Discuss how to do formal proofs about safety of runtime code updates

- Using a (relatively) new variant of Hoare logic

- specifically, Hoare logic with nested Hoare triples

• Example of formally specifying safety of a runtime update

- for a model of an updateable web server from:

“Formalizing Dynamic Software Updating”

(Gavin Bierman, Michael Hicks, Peter Sewell, Gareth Stoyle)

Outline

• Discuss how to do formal proofs about safety of runtime code updates

- Using a (relatively) new variant of Hoare logic

- specifically, Hoare logic with nested Hoare triples

• Example of formally specifying safety of a runtime update

- for a model of an updateable web server from:

“Formalizing Dynamic Software Updating”

(Gavin Bierman, Michael Hicks, Peter Sewell, Gareth Stoyle)

- (time permitting) glimpse of this proof done in Crowfoot, our semi-automated verification tool

Hoare logic

• A formal logic for proving things - triples - about programs, e.g.

• Meaning: if we run the program in a state where the precondition holds

- then the program doesn’t crash

- and if it terminates, the postcondition will hold

Hoare logic

• A formal logic for proving things - triples - about programs, e.g.

• Meaning: if we run the program in a state where the precondition holds

- then the program doesn’t crash

- and if it terminates, the postcondition will hold [ ] = heap access (indirection)

Hoare logic

• A formal logic for proving things - triples - about programs, e.g.

• Meaning: if we run the program in a state where the precondition holds

- then the program doesn’t crash

- and if it terminates, the postcondition will hold

• BUT: Conventional Hoare logic assumes that program’s code is fixed

- because pre- and post-condition talk only about data, not code

- so how can one reason about dynamic software updates?

Key idea: nested Hoare triples

• Writing code onto the heap:

Key idea: nested Hoare triples

• Writing code onto the heap:

Key idea: nested Hoare triples

• Writing code onto the heap:

Key idea: nested Hoare triples

• Writing code onto the heap:

• Invoking code stored on the heap:

Key idea: nested Hoare triples

• Writing code onto the heap:

• Invoking code stored on the heap:

Key idea: nested Hoare triples

• Writing code onto the heap:

• Invoking code stored on the heap:

Nested Hoare triples

• Only understood recently

- because underlying mathematics is complicated

- started with [Honda, Yoshida, Berger - LICS 05]

- further developed by others since then

Nested Hoare triples

• Only understood recently

- because underlying mathematics is complicated

- started with [Honda, Yoshida, Berger - LICS 05]

- further developed by others since then

• Now we have the theory, can we use it to reason about programs?

- We thought: let’s give it a go

Nested Hoare triples

• Only understood recently

- because underlying mathematics is complicated

- started with [Honda, Yoshida, Berger - LICS 05]

- further developed by others since then

• Now we have the theory, can we use it to reason about programs?

- We thought: let’s give it a go

• We borrowed an example from the literature: a model of an updateable web server

- One particular runtime update: adding logging to the web server

- Code on slides is mercilessly pruned compared to our paper

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

This slide: initial version of model web server

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1; q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

Record that we start at version 1

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q); [loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

Create a queue for events

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_); eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

‘loop’ procedure kept on theheap, so we can update it later

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e); call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update(); eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

heapcell loop_h;

heapcell version;

proc main()

{

locals q;

[version] := 1;

q := new 0;

call mkQueue(q);

[loop_h] := loop(_);

eval [loop_h](q)

}

proc loop(q)

{

locals e;

e := new 0;

call getEvent(q,e);

call handleEvent(e);

call maybe_update();

eval [loop_h](q)

}

proc handleEvent(e) {...}

proc getEvent(q,e) {...}

proc mkQueue(q) {...}

What does our update do?

• Add logging to server – every event will be logged

• Overwrite ‘loop’ code with a new version

• Add three new procedures:

- loopPrime – the new event-handling loop

- logEvent – creates a log entry for a given event

- mkEmptyLog – used during transition to create an empty log

heapcell loopPrime_h;

heapcell logEvent_h;

heapcell mkEmptyLog_h;

proc mkEmptyLog_2(log) {...}

proc logEvent_2(e,log) {...}

proc loop_2(q)

{

locals log;

log := new 0;

eval [mkEmptyLog_h](log);

eval [loopPrime_h](q,log)

}

proc loopPrime_2(q,log)

{

locals e;

e := new 0;

call getEvent(q,e);

eval [logEvent_h](e,log);

call handleEvent(e);

call maybe_update();

eval [loopPrime_h](q,log)

}

heapcell loopPrime_h;

heapcell logEvent_h;

heapcell mkEmptyLog_h;

proc mkEmptyLog_2(log) {...}

proc logEvent_2(e,log) {...}

proc loop_2(q){

locals log;

log := new 0;

eval [mkEmptyLog_h](log);

eval [loopPrime_h](q,log)

}

proc loopPrime_2(q,log)

{

locals e;

e := new 0;

call getEvent(q,e);

eval [logEvent_h](e,log);

call handleEvent(e);

call maybe_update();

eval [loopPrime_h](q,log)

}

Replacement for ‘loop’:“transitional function” to take system into new version

heapcell loopPrime_h;

heapcell logEvent_h;

heapcell mkEmptyLog_h;

proc mkEmptyLog_2(log) {...}

proc logEvent_2(e,log) {...}

proc loop_2(q)

{

locals log;

log := new 0;

eval [mkEmptyLog_h](log); eval [loopPrime_h](q,log)

}

proc loopPrime_2(q,log)

{

locals e;

e := new 0;

call getEvent(q,e);

eval [logEvent_h](e,log);

call handleEvent(e);

call maybe_update();

eval [loopPrime_h](q,log)

}

Set up an empty Log structure

heapcell loopPrime_h;

heapcell logEvent_h;

heapcell mkEmptyLog_h;

proc mkEmptyLog_2(log) {...}

proc logEvent_2(e,log) {...}

proc loop_2(q)

{

locals log;

log := new 0;

eval [mkEmptyLog_h](log);

eval [loopPrime_h](q,log)}

proc loopPrime_2(q,log)

{

locals e;

e := new 0;

call getEvent(q,e);

eval [logEvent_h](e,log);

call handleEvent(e);

call maybe_update();

eval [loopPrime_h](q,log)

}

Pass control to the newevent handling loop

heapcell loopPrime_h;

heapcell logEvent_h;

heapcell mkEmptyLog_h;

proc mkEmptyLog_2(log) {...}

proc logEvent_2(e,log) {...}

proc loop_2(q)

{

locals log;

log := new 0;

eval [mkEmptyLog_h](log);

eval [loopPrime_h](q,log)

}

proc loopPrime_2(q,log){

locals e;

e := new 0;

call getEvent(q,e);

eval [logEvent_h](e,log); call handleEvent(e);

call maybe_update();

eval [loopPrime_h](q,log)

}

new event handling loop

The new behaviour:Log every eventbefore processing

proc maybe_update()

{

if [version] = 1 then

{

if nondet then { skip }

else

{

[version] := 2;

[loop_h] := loop_2(_);

[loopPrime_h] := loopPrime_2(_,_);

[logEvent_h] := logEvent_2(_,_);

[mkEmptyLog_h] := mkEmptyLog_2(_)

}

}

else { skip }

}

Non-deterministically decide whether to update or not

proc maybe_update()

{

if [version] = 1 then

{

if nondet then { skip }

else

{

[version] := 2; [loop_h] := loop_2(_);

[loopPrime_h] := loopPrime_2(_,_);

[logEvent_h] := logEvent_2(_,_);

[mkEmptyLog_h] := mkEmptyLog_2(_)

}

}

else { skip }

}

Increment version number

proc maybe_update()

{

if [version] = 1 then

{

if nondet then { skip }

else

{

[version] := 2;

[loop_h] := loop_2(_);

[loopPrime_h] := loopPrime_2(_,_);

[logEvent_h] := logEvent_2(_,_);

[mkEmptyLog_h] := mkEmptyLog_2(_)

}

}

else { skip }

}

Overwrite ‘loop’ with new version

proc maybe_update()

{

if [version] = 1 then

{

if nondet then { skip }

else

{

[version] := 2;

[loop_h] := loop_2(_);

[loopPrime_h] := loopPrime_2(_,_);

[logEvent_h] := logEvent_2(_,_);

[mkEmptyLog_h] := mkEmptyLog_2(_) }

}

else { skip }

}

Load newprocedures onto heap

What are we trying to prove?

We need to say what we are trying to prove!

- For each procedure we give a specification e.g. for ‘getEvent’:

- Concentrate on memory safety

- i.e. the right kind of data structures are present in the right place

- We won’t go into details of Queue, Event, Log predicates...

To specify the effect of maybe_update(), we need this monster definition

Code(v) describes the kind of code present on the heap in version v

Code(v) is nested inside itself!

Specifications

For ‘loop’:

Specifications

For ‘loop’:

For ‘logEvent_2’:

Specifications

For ‘loop’:

For ‘logEvent_2’:

For ‘maybe_update’:

• With these specifications we can prove the update safe

• Proof done semi-automatically by our verification tool

Summary

• Discussed how to do formal proofs about safety of runtime code updates

- using Hoare logic with nested triples

• Talked through how to formally specify safety of an update

- for a model of an updateable web server from:

“Formalizing Dynamic Software Updating”

(Bierman, Hicks, Sewell, Stoyle)

• (maybe) glimpsed Crowfoot, our semi-automated verification tool for such safety proofs

The End