Download presentation
Presentation is loading. Please wait.
Published byJoshua O’Neal’ Modified over 9 years ago
1
Formal reasoning about runtime code update Billiejoe (Nathaniel) Charlton Ben Horsfall Bernhard Reus HotSWUp 2011
2
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
3
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)
4
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
5
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
6
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)
7
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?
8
Key idea: nested Hoare triples Writing code onto the heap:
9
Key idea: nested Hoare triples Writing code onto the heap:
10
Key idea: nested Hoare triples Writing code onto the heap:
11
Key idea: nested Hoare triples Writing code onto the heap: Invoking code stored on the heap:
12
Key idea: nested Hoare triples Writing code onto the heap: Invoking code stored on the heap:
13
Key idea: nested Hoare triples Writing code onto the heap: Invoking code stored on the heap:
14
Nested Hoare triples Only understood recently -because underlying mathematics is complicated -started with [Honda, Yoshida, Berger - LICS 05] -further developed by others since then
15
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
16
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
17
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
18
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) {...}
19
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
20
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
21
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
22
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) {...}
23
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) {...}
24
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) {...}
25
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) {...}
26
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) {...}
27
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
28
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) }
29
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
30
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
31
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
32
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
33
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
34
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
35
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
36
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
37
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...
38
To specify the effect of maybe_update(), we need this monster definition
39
Code(v) describes the kind of code present on the heap in version v
40
Code(v) is nested inside itself!
41
Specifications For ‘loop’:
42
Specifications For ‘loop’: For ‘logEvent_2’:
43
Specifications For ‘loop’: For ‘logEvent_2’: For ‘maybe_update’:
44
With these specifications we can prove the update safe Proof done semi-automatically by our verification tool
45
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
46
The End
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.