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

## Presentation on theme: "Formal reasoning about runtime code update Billiejoe (Nathaniel) Charlton Ben Horsfall Bernhard Reus HotSWUp 2011."— Presentation transcript:

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 the heap, 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 new event 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 event before 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 new procedures 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

Download ppt "Formal reasoning about runtime code update Billiejoe (Nathaniel) Charlton Ben Horsfall Bernhard Reus HotSWUp 2011."

Similar presentations