• No results found

Example

In document Object code verification (Page 94-103)

3.4 Proof Rules for Commands

3.4.1 Example

Specifying commands in terms of logical formulas, as in the wp calculus, allows the verification to be based on the properties required of the commands. Reasoning about commands specified using the wp function is similar to reasoning in any wp calculus (e.g. see Dijkstra, 1976 or Gries, 1981). The examples here will consider the features specific to the languageL. For the examples, assume

P;Q

2 A,

c;c

1 2 C0,

x;y;z

2 Names,

v;v

1

;v

2 2 Values,

e

1

;e

2 2 E and

l;l

1

;l

2

2Labels. Also assume

x;y;z

and the program counter pc are all distinct.

Assignments: The assignment command

x;y

:=

v

1

;v

2

;l

can be shown to establish postcondition

x

=a

v

1: `true )wp((

x;y

:=

v

1

;v

2

;l

)

;x

=a

v

1

). First, the precondition is strengthened with the assertion

x

=a

v

1

/

(pc

;l

)(

x;v

1 )(

y;v

2

), which requires a proof of`

x

=a

v

1

/

(pc

;l

) (

x;v

1 )(

y;v

2

) ) true. This can be established using the substitution rules of Figure (3.3) and Lemma (3.1) (for the properties of the equivalence relation). The substitution is pushed into the equality (since

v

1

2Values) to obtain`

x/

(pc

;l

)(

x;v

1

)(

y;v

2

)=a

v

1. Since

x

is distinct from both

y

and pc, this reduces to`

v

1 =a

v

1, which is trivially true. The specification to be proved is then`

x

=a

v

1

/

(pc

;l

)(

x;v

1 )(

y;v

2 ))wp((

x;y

:=

v

1

;v

2

;l

)

;x

=a

v

1 ). This is immediate from the assignment rule (tl1) (the assignment list is always correct since the names are distinct).

Flow of control: The selection of commands for execution is determined by the labelling com-

mand and the program counter pc. For labelled command

l

:

c

2 C to satisfy the specification `

P

) wp(

l

:

c;Q

), the precondition

P

must satisfy`

P

) pc =a

l

(from the label rule tl2). For command

c

1 to select

l

:

c

as a successor,

c

3.4 Proof Rules for Commands 77 Assignment: `

P /

(pc

;l

)

al

)correct?((pc

;l

)

al

) `

P /

((pc

;l

)

al

))wp(:=(

al;l

)

;P

) (tl1) Label: `

P

)pc=a

l

^wp(

c;Q

) `

P

)wp(

l

:

c;Q

) (tl2) Conditional: `

P

^

b

)wp(

c

1

;Q

) `

P

^:

b

)wp(

c

2

;Q

) `

P

)wp(if

b

then

c

1else

c

2

;Q

) (tl3) Strengthening: `

R

)

Q

`

P

)wp(

c;R

) `

P

)wp(

c;Q

) (tl4) Weakening: `

P

)

R

`

R

)wp(

c;Q

) `

P

)wp(

c;Q

) (tl5) where

c;c

1

;c

2 2C0,

l

2El,

b

2Eb,

al

2Alist and

P;Q;R

2A Figure 3.5: Proof Rules for Commands ofL

satisfies`

P

)wp(

c;Q

)and

Q

is the precondition for

l

:

c

then`

Q

)pc=a

l

. This must be established using the assignment rule (tl1), since pc is a name. If

c

1 is

:=(

al;l

)then the proof is of the assertions`

P

)

Q/

(pc

;l

)

al

(for the strengthening rule tl4) and`

Q

)

Q/

(pc

;l

)

al

(for the assignment rule tl1).

Derived commands: Reasoning about derived commands is based on their definition. The com-

mand goto

l

can always be shown to satisfy the specification`

P /

(pc

;l

))wp(goto

l;P

). The truth of this follows by definition of goto, which reduces the specification to `

P /

(pc

;l

) )

wp(:= (nil

;l

)

;P

), and from the assignment rule (tl1). The command abort satisfies the specifi- cation`false)wp(abort

;Q

), for any postcondition

Q

2A. By definition abort =(pc

;

pc:=

true

;

false

;

undef(El)). Since the assignment list is never correct in any state, the command abort can never terminate.

Instructions: Processor instructions are specified in terms of L commands. The Alpha AXP instruction addl r1

;

r2

;

r3 is modelled as the command r3

:= r 1

+a r 2

;

loc

(pc+a 1). Using the assignment rule (tl1), the instruction can be shown to satisfy the specification: `

P

)

wp(r 3 := r 1 +a r 2

;

loc (pc +a 1)

;

r 3 =a r 1 +a r 2

^ pc =a loc(pc +a 1)), for any

P

2 A. The specification of the Alpha AXP jump instruction jmp r

;v

can also be derived from the assignment rule. The instruction is modelled by theL command r := pc

;

loc(

v

)and specified: `

P

^pc =a

l

) wp((r := pc

;

loc(

v

))

;

r =a

l

^pc =a loc(

v

)), for any

P

2 P. Note that

3.4 Proof Rules for Commands 78

because the value of the program counter changes, the constant label

l

must be used to fix the value of pc which is to be assigned to r.

Abstraction

To see the effect on verification of abstracting commands, assume the commands goto

l

and (

l

:

x;y

:=

e

1

;e

2

;l

1

) are executed in sequence. To show that the commands establish

y

=a

e

2 requires a proof of the two assertions:

`true)wp(goto

l;

pc=a

l

) `pc=a

l

)wp((

l

:

x;y

:=

e

1

;e

2

;l

1 )

;y

=a

e

2 )

Abstracting from the commands replaces these two assertions with a single specification. Let

c

= (goto

l

;(

l

:

x;y

:=

e

1

;e

2

;l

1

)). By definition, this is the command if pc

/

(pc

;l

) =a

l

then

x;y

:=

e

1

;e

2

;l

1else goto

l

. Because pc

/

(pc

;l

) =a

l

is true in any state, command

c

can be simplified to

x;y

:=

e

1

;e

2

;l

1. The specification to be proved is therefore:

` true )

wp((

x;y

:=

e

1

;e

2

;l

1

)

;y

=a

e

2

), which is straightforward from the assignment rule (tl1). For a second example, consider the following sequence of Alpha AXP instructions:

l

1 :addlr3

;

r2

;

r3

l

2 :cmpeqr3

;

r4

;

r0

l

3 :beqr0

;v

The labels

l

1

;l

2

;l

3are assumed to satisfy

l

2

loc(

l

1 +a1)and

l

3 loc(

l

2 +a1). The sequence is verified by replacing each instruction with its equivalent command of L. To show that the sequence passes control to label loc(

v

), if execution begins in a state satisfying r3 a r2=a r4, requires a proof of the assertions:

`pc=a

l

1 ^r3 a r2=a r4 )wp((

l

1 :r3:=r3+a r2

;

loc(pc+a1))

;

pc=a

l

2 ^r3=a r4) `pc=a

l

2 ^r3=a r4 ) wp((

l

2 :if r3=a r4 then r0:=1

;

loc(pc+a1) else r0:=0

;

loc(pc+a1))

;

pc=a

l

3 ^:r0=a 1) `pc=a

l

3 ^r0=a 1 )wp(

l

3

:if r0 =a 1then goto loc(

v

)else goto loc(pc+a1)

;

pc=a loc(

v

))

The proof of each assertion is straightforward. However, the proof of the sequence can be sim- plified by abstracting from theLcommands.

Let

c

1

;c

2

;c

3 be the

L commands modelling the instructions labelled

l

1

;l

2

;l

3 respectively. The abstraction

c

of the sequence is obtained by the composition of

c

1 and

c

2, followed by composition with

c

3:

c

=(

c

1 ;

c

2 );

c

3. After simplification, this is the command:

l

1

:if r3+a r2 =a r4 then r3

;

r0:=r3+a r2

;

1

;

loc(

v

)

else r3

;

r0:=r3+a r2

;

0

;

loc(

l

3

3.5 Conclusion 79

The specification of the sequence can then be verified as a specification of the command: `pc=a

l

1 ^r3=a r4 )wp(

l

1 :if r3+a r2=a r4 then r3

;

r0:=r3+a r2

;

1

;

loc(

v

) else r3

;

r0:=r3+a r2

;

0

;

loc(

l

3 +a1)

;

pc=a loc(

v

))

The proof of this specification is straightforward from the precondition and the proof rules. The specifications to be established in the proof based on the individual commands were mainly con- cerned with propagating properties through the sequence of commands. For example, whether pc was assigned loc(

v

)in command

c

3 depended on the result of the test in command

c

2. Because many of these properties could be determined from the syntax of commands, constructing and simplifying an abstraction of the sequence leads to a simpler proof. The abstraction describes all the properties established during the execution of the sequence. The final proof is therefore concerned only with the pre- and postcondition of the sequence and not with the intermediate assertions established by commands in the sequence.

3.5

Conclusion

To model and abstract object code in terms ofL, the commands ofLmust be expressive enough to model any processor instruction and to support the abstraction of commands. To verify ob- ject code modelled in terms of L also requires the ability to define proof rules of a program logic which can be applied to the commands of L. The principal difficulty when considering processor instructions is the undecidability introduced by pointers and computed jumps. A pro- cessor instruction carries out data operations and makes possibly conditional assignments to one or more program variables, which may be identified by pointers. These assignments are simulta- neous, requiring a method of detecting unexecutable commands in the presence of pointers. To define proof rules for the commands requires a substitution operator which takes into account the aliasing problem. The abstraction of commands must consider both the aliasing problem, when considering lists of assignments, and computed jumps, when determining whether two commands are executed in sequence.

To model the behaviour of processor instructions, the language L contains a conditional, a labelling and an assignment command. The data operations of a processor are modelled as expressions of L, which are defined in terms of constants and functions. These include the name expressions, which are a more general model of pointers than those commonly used (e.g. Dijkstra, 1976, Manna & Waldinger, 1981). The name expressions ofLprovide flexibility when modelling processor memory operations and allow pointers and simple (constant) variables to be treated equally. The assignment command ofL is a simultaneous assignment and is expressive enough to describe any state transformation. This allows any processor instruction to be modelled by a singleLcommand. The approach used to detect unexecutable assignments is similar to that of Cartwright & Oppen (1981). However, Cartwright & Oppen (1981) restricted the expressions which could be assigned to variables to exclude substitutions; these restrictions are not required

3.5 Conclusion 80

for the commands ofL. Instructions implement the execution model of a processor, by selecting instructions for execution. The execution model ofLis based on the use of a program counter pc to select a command for execution. Each command ofLassigns a value to the program counter and this value can be the result of an expressions. The language L is therefore a flow-graph language which includes computed jumps.

The abstraction of commands is based on manipulating the syntax of L commands to con- struct a new command. For this approach, sequential composition was defined as a function on commands ofL. This differs from the usual treatment of sequential composition as a primitive syntactic construct which represents the combination of commands. The definition of sequential composition here extended the rules of Hoare et al. (1987) to the commands of L, a language which includes pointers and computed jumps. To abstract from assignment commands required both substitution and the combination of assignment lists in the presence of name expressions. The approach used separates the syntactic constructs, representing the operations of substitution and combination, from the semantics of these constructs, which carry out the operations in a given state. This overcomes the undecidability of pointers and computed jumps by allowing the result of the operations to be determined as part of a correctness proof. The syntactic constructs allow the abstraction of assignment commands to be described (and constructed) from the text of the commands. The abstraction of computed jumps uses the conditional command and the pro- gram counter pc to guard the execution of commands. This ensures that the flow of control from one command to another is accurately described in the abstraction of the two commands. Con- sequently, sequential composition can be applied to any two commands: the result will always be an abstraction of the commands.

To specify and reason about commands of L, a wp predicate transformer was defined to construct an assertion on a state. Proof rules are defined using the specifications formed by this wp function, following the approach of Dijkstra (1976). The majority of the proof rules of Figure (3.5) are standard rules for commands (e.g. Gries, 1981). However, the proof rule for the assignment command ofL required the use of the substitution operator ofL, which can be applied in the presence of name expressions. Reasoning about substitution expressions based on the syntax of expressions is possible using the rules of Figure (3.3). The languageLdiffers from the flow-graph languages usually considered in verification (see e.g. Clint & Hoare, 1972, Loeckx & Sieber, 1987 or Francez, 1992) in that the program counter pc is a program variable. This allows its use when specifyingLcommands and simplifies the proof rules (for the labelling and assignment commands) which describe the execution model ofL.

The main contribution of this chapter is the development of the commands of Lwhich can be used to model processor instructions and the definition of a method of abstracting arbitraryL commands. The languageLis independent of any processor: no distinction is made between in- structions of the same processor and instructions of different processors. This is possible because processor instructions have a similar semantics. Modelling instructions in terms ofL provides the ability to simplify verification by abstracting from the commands of a program. The abstrac- tion of commands is constructed from the text of the commands, an approach which allows for efficient mechanisation. This reduces the manual work needed to verify a program by providing

3.5 Conclusion 81

a means for the use of automated tools to abstract and simplify sequences of commands in the program.

Chapter 4

Programs

An object code program is verified by showing that states which are produced during its execu- tion have the properties required by the program specification. The execution of an object code program is based on the execution of the program’s instructions. Both the selection of instruc- tions for execution and the states produced during execution are determined by the actions of the program instructions; the object code program only determines the instructions which are avail- able for execution. Object code is verified in terms of its model as a program of the languageL, to allow the methods of verification and abstraction to be independent of the processor language. The translation from a processor language to the languageLis straightforward: a program ofL which models object code is simply the set ofLcommands modelling the instructions.

A program ofLis verified by reasoning about the individual program commands and the ver- ification is simplified by abstracting from the program. The main difficulty with programs ofLis the abstraction of programs, which will be constructed by applying program transformations. To use these transformations in verification they must be shown to preserve the correctness of a pro- gram. The correctness of a transformation is established by reasoning about the changes made by the transformation to the program behaviour. The techniques used in verification to reason about program transformation generally assume that the programming language imposes a syn- tactic structure on programs which can be used to define transformations. These languages also allow comparisons to be made between the behaviour of programs without the need to consider the behaviour of individual program commands, simplifying reasoning about transformations. Programs of L do not have a syntactic structure with which to define transformations and the behaviour ofLprograms must be compared by considering the effect of each program command individually. To define and reason about transformations on programs ofLtherefore requires an extension of the methods used in program verification.

The abstraction ofLprograms will be based on the sequential composition operator of Chap- ter 3. The simplest method of abstraction is to apply sequential composition to manually chosen program commands. Any program can be abstracted in this way but the size of object code pro- grams means that this approach is not enough to reduce the work needed to verify a program. A second approach is to use the flow of control through a program to mechanically find the se-

4.1 Programs ofL 83

quences of commands which are to be abstracted by sequential composition. This approach can be described in terms of program transformations which can be efficiently automated, the tech- niques required are common in code optimisation. However, it requires a method for showing that the program transformations are correct. A framework will be described in which the cor- rectness of a program transformation can be established. The framework is based on a method for using the flow-graph of a program to impose a syntactic structure on the program. This structure can then be used to define and reason about program transformations.

To verifyLprograms, a program logic suitable for reasoning about the liveness properties of a program will be described. This is based on the assertion languageAof Chapter 3, extended with a specification operator for programs. Proof rules for this specification operator allow the programs ofLto be verified using the method of intermittent assertions (Manna, 1974; Burstall, 1974). The properties of program commands can be specified and verified using the wp function and proof rules of Chapter 3. The proof rules for programs will also support the abstraction of a program to simplify its verification and an approach to using program abstraction when verifying programs will be described.

The chapter begins with the definition in Section 4.1 of the syntax and semantics ofL pro- grams. This includes a refinement relation between programs of L and a method for program abstraction based on individually chosen commands. Section 4.2 describes the framework used to define and reason about the transformations for program abstraction. Transformations are defined on a region of a program: a group of commands with a structure defined by their flow- graph. The transformations on regions, used to abstract programs, are defined and their properties described in Section 4.3. The specification and verification ofLprograms is described in Sec-

tion 4.4. The method used to verify a program will be described and a small program verified as

an example.

4.1

Programs of

L

An object code program has a finite number of instructions, each of which is stored in a memory location identified by a unique address (the label of the instruction). Instructions can be added to an object program by storing an instruction at an address which does not identify any other instruction. These properties are preserved by programs of L, which are finite sets of uniquely labelled L commands. This is enough to model a program of a processor language since a command of L can model an instruction. Program commands are indexed by the labels: if a program

p

contains command

l

:

c

then the label

l

uniquely identifies that command and is enough to obtain command

l

:

c

from

p

.

Definition 4.1 Programs

A finite set

p

of labelled commands is a program ofLiff program?(

p

)is true.

program?:FiniteSet(C)!boolean

In document Object code verification (Page 94-103)