Proving Properties on Programs From the Coq Tutorial at ITP 2015

Similar documents
An Annotated Language

A Certified Verifier for a Fragment of Separation Logic

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

A CRASH COURSE IN SEMANTICS

Part II. Hoare Logic and Program Verification. Why specify programs? Specification and Verification. Code Verification. Why verify programs?

CIS 500 Software Foundations. Final Exam. May 3, Answer key

Note that in this definition, n + m denotes the syntactic expression with three symbols n, +, and m, not to the number that is the sum of n and m.

Abstract Interpretation

The Formal Semantics of Programming Languages An Introduction. Glynn Winskel. The MIT Press Cambridge, Massachusetts London, England

Infinite Objects and Proofs 1

Induction and Semantics in Dafny

Denotational Semantics. Domain Theory

Lecture 5 - Axiomatic semantics

CS2104 Prog. Lang. Concepts

6. Hoare Logic and Weakest Preconditions

Inductive data types

Coq projects for type theory 2018

Semantics and Verification of Software

Inductive Definitions, continued

Hoare logic. WHILE p, a language with pointers. Introduction. Syntax of WHILE p. Lecture 5: Introduction to separation logic

Hoare logic. Lecture 5: Introduction to separation logic. Jean Pichon-Pharabod University of Cambridge. CST Part II 2017/18

Theorem proving. PVS theorem prover. Hoare style verification PVS. More on embeddings. What if. Abhik Roychoudhury CS 6214

Verified Characteristic Formulae for CakeML. Armaël Guéneau, Magnus O. Myreen, Ramana Kumar, Michael Norrish April 27, 2017

Introduction to Denotational Semantics. Class Likes/Dislikes Survey. Dueling Semantics. Denotational Semantics Learning Goals. You re On Jeopardy!

Hoare Logic. COMP2600 Formal Methods for Software Engineering. Rajeev Goré

Exercises on Semantics of Programming Languages

Handout 9: Imperative Programs and State

CIS 500: Software Foundations

Refinements to techniques for verifying shape analysis invariants in Coq

CSE505, Fall 2012, Midterm Examination October 30, 2012

Introduction to Denotational Semantics. Brutus Is An Honorable Man. Class Likes/Dislikes Survey. Dueling Semantics

CMSC 330: Organization of Programming Languages

CIS 500 Software Foundations. Midterm I. (Standard and advanced versions together) October 1, 2013 Answer key

Contents. Chapter 1 SPECIFYING SYNTAX 1

CS 456 (Fall 2018) Scribe Notes: 2

Now that we have keys defined for the maps, we can start talking about maps. The first of these are Total Maps. Total Maps are defined as follows:

Goals: Define the syntax of a simple imperative language Define a semantics using natural deduction 1

Principles of Programming Languages

Hoare logic. A proof system for separation logic. Introduction. Separation logic

Automated Reasoning. Natural Deduction in First-Order Logic

Outline. What is semantics? Denotational semantics. Semantics of naming. What is semantics? 2 / 21

Introduction to Axiomatic Semantics

Reasoning About Imperative Programs. COS 441 Slides 10

Semantics with Applications 3. More on Operational Semantics

Getting Started with AutoCorres

PROGRAM ANALYSIS & SYNTHESIS

Lecture Notes: Hoare Logic

Program Analysis: Lecture 02 Page 1 of 32

Hoare Logic and Model Checking

An Intrinsic Encoding of a Subset of C and its Application to TLS Network Packet Processing

A Simple Supercompiler Formally Verified in Coq

Denotational Semantics

Chapter 3 (part 3) Describing Syntax and Semantics

Certifying Assembly with Formal Security Proofs: the Case of BBS

No model may be available. Software Abstractions. Recap on Model Checking. Model Checking for SW Verif. More on the big picture. Abst -> MC -> Refine

Main Goal. Language-independent program verification framework. Derive program properties from operational semantics

Semantics. There is no single widely acceptable notation or formalism for describing semantics Operational Semantics

Introduction to dependent types in Coq

A Hoare Logic Contract Theory: An Exercise in Denotational Semantics

Chapter 3. The While programming language

CIS 500: Software Foundations

COMP 507: Computer-Aided Program Design

Analysis of dependent types in Coq through the deletion of the largest node of a binary search tree

Why. an intermediate language for deductive program verification

Chapter 3. Describing Syntax and Semantics ISBN

Transforming Programs into Recursive Functions

Program certification with computational

Hoare Logic: Proving Programs Correct

Software Quality Assurance

In Our Last Exciting Episode

Proving Properties of Recursive Functions and Data Structures. CS 270 Math Foundations of CS Jeremy Johnson

1.3. Conditional expressions To express case distinctions like

Hoare triples. Floyd-Hoare Logic, Separation Logic

c constructor P, Q terms used as propositions G, H hypotheses scope identifier for a notation scope M, module identifiers t, u arbitrary terms

Coq quick reference. Category Example Description. Inductive type with instances defined by constructors, including y of type Y. Inductive X : Univ :=

A Simpl Shortest Path Checker Verification

Comp 411 Principles of Programming Languages Lecture 7 Meta-interpreters. Corky Cartwright January 26, 2018

CSCI.6962/4962 Software Verification Fundamental Proof Methods in Computer Science (Arkoudas and Musser) Section 17.2

02157 Functional Programming Interpreters for two simple languages including exercises

Equations: a tool for dependent pattern-matching

Verification Condition Generation

Chapter 3. Describing Syntax and Semantics ISBN

Chapter 3. Describing Syntax and Semantics

Compiler Construction

Program Calculus Calculational Programming

Modular dependent induction in Coq, Mendler-style. Paolo Torrini

Introduction to Co-Induction in Coq

The Rule of Constancy(Derived Frame Rule)

The University of Nottingham SCHOOL OF COMPUTER SCIENCE A LEVEL 2 MODULE, SPRING SEMESTER COMPILERS ANSWERS. Time allowed TWO hours

Big-step Operational Semantics (aka Natural Semantics)

CS422 - Programming Language Design

Programming Languages Fall 2013

Semantics. A. Demers Jan This material is primarily from Ch. 2 of the text. We present an imperative

CIS 500: Software Foundations

Expr_annotated.v. Expr_annotated.v. Printed by Zach Tatlock

Hoare Logic and Model Checking

Assignment #4: Lambda Calculus & Semi-automatic Program Verification. You are allowed to work on this assignment in pairs. Hand in Requirements:

Introduction to Axiomatic Semantics (1/2)

Formal Verification of the Heap Manager of an Operating System using Separation Logic

Chapter 3. Describing Syntax and Semantics

Transcription:

Proving Properties on Programs From the Coq Tutorial at ITP 2015 Reynald Affeldt August 29, 2015 Hoare logic is a proof system to verify imperative programs. It consists of a language of Hoare triples {P }c{q} where c is a program, P is a pre-condition, and Q is a post-condition, that the execution of a program is supposed to respect. Here, we provide a Hoare logic for a simple imperative language (while-loops and parameterless procedure calls) and apply it to the verification of programs computing the factorial function. The main reference is [1]. This lecture introduces examples of inductive data types and predicates (following the previous lectures). It provides a simple example of Coq modules. We rely only on Coq s standard library. In particular, we use finite maps and sets (in the sense of Ensemble). Overview We first define a functor with a generic Hoare logic: syntax of the language in Sect. 1.2, operational semantics in Sect. 1.3, and Hoare logic in Sect. 1.4. We state the soundness of Hoare logic in Sect. 1.5. We instantiate this module with the basic instruction of variable assignment in Sect. 2.1, and apply the result to two proofs of the factorial program: one using a while-loop (Sect. 2.2) and one using recursive procedure calls (Sect. 2.3). Contents 1 A Generic Hoare Logic 2 1.1 The Setting.............................. 2 1.2 Syntax................................. 3 1.3 Operational Semantics........................ 3 1.4 Hoare Logic.............................. 5 1.5 Soundness of Hoare Logic...................... 6 2 Verification of Concrete Imperative Programs 7 2.1 Instantiation............................. 7 2.2 Factorial as a While-loop...................... 8 2.3 Factorial as a Recursive Function.................. 9 1

About the Exercise The student is expected to complete the holes in the various proofs of the companion Coq file. It is maybe more interesting to start with the example in module FactorialWhile, then the one in module FactorialRec. All the intermediate assertions are provided. Afterwards, move to the proofs in functor Cmd. Most proofs are simple inductions w.r.t. the operational semantics; the main step and/or hints are provided so that one can just exercise with Coq tactics. The soundness proof is non-trivial and can be done last. The main induction principle is provided. 1 A Generic Hoare Logic 1.1 The Setting We assume the following abstract type state, wrapped in the following interface: Module Type STATE. Parameter state : Set. Definition assert := state Prop. Definition entails ( P Q : assert ) : Prop := s, P s Q s. Parameter exp : Set. Parameter bexp : Set. Parameter eval : exp state nat. Parameter beval : bexp state Prop. End STATE. Functions of type assert are used to specify states in pre/post-conditions of Hoare logic. All the standard logical connectives can be lifted from Prop to assert. In particular, entails defines the lifting of the Coq implication to assert and it is denoted by ===>. exp is intended to be the type of arithmetic expression and bexp is intended to be the type of boolean expressions. eval (resp. beval) is an evaluation function for expressions in a given state and is denoted by [ e ]_s (resp. [ b ]b_s). We do not need a concrete definition at this point. We also assume the following abstract type cmd0 for basic commands (such as assignment, memory lookup, etc.): Module Type CMD0 ( S : STATE ). Parameter cmd0 : Set. Parameter exec0 : S. state cmd0 option S. state Prop. Parameter hoare0 : S. assert cmd0 S. assert Prop. End CMD0. The ternary relation exec0 is the operational semantics. A None state represents an execution error. The ternary relation hoare0 is intended to be the 2

corresponding Hoare logic. We do not need concrete definitons at this point. Our functor for Hoare logic start with the declaration Module Cmd ( S : STATE ) ( Cmd0 : CMD0 S ).... whose body is the topic of the next sections. 1.2 Syntax The language we are dealing with is Winskel s While language extended with parameterless procedures. We first provide a type for procedure names. Since they will be used as an environment for the semantics, we use the finite maps from the Coq standard library: Module Procs := FMapList. Make ( String_as_OT ). Definition proc := Procs. key. In this case, proc is just string. The BNF for the language is unsurprising: c ::= basic c 0 c 1.; c 2 If b Then c 1 Else c 2 while b c call p It is encoded simply as an inductive type, each production rule taking the form of a constructor: Inductive cmd : Type := basic : Cmd0. cmd0 cmd seq : cmd cmd cmd ifte : bexp cmd cmd cmd while : bexp cmd cmd call : proc cmd. In the following, sequences are noted by.; and conditional branching is denoted by If... Then... Else. 1.3 Operational Semantics The operational semantics is defined by a ternary predicate exec of type state cmd option state Prop with a parameter, say l, for the environment of procedures. It is denoted by l ~ s >- c ---> s : s is state before execution of c, and s is the state after execution. The environment of procedures is a map from procedure names to their bodies, i.e., from type proc to type cmd: 3

Definition procs := Procs. t cmd. The operational semantics is syntax-oriented: it is defined by an inductive predicate and for each syntactic construct of the language there is one or two constructors (depending on branching behavior or error states). Let us first lookup at the execution rules in their traditional, pencil-and-paper presentation: exec0 s c 0 s 1 l ~s >- basic c 0 ---> s 1 exec basic l ~s >- c ---> Some s 1 l ~s 1 >- d ---> s 2 exec seq l ~s >- c.; d ---> s 2 [ b ]b_s l ~s >- c ---> s 1 exec ifte true l ~s >- If b Then c Else d ---> s 1 [ b ]b_s l ~s >- c ---> Some s 1 l ~s 1 >- while b c ---> s 2 while true l ~s >- while b c ---> s 2 l p c l ~s >- c ---> s 1 exec call l ~s >- call p ---> s 1 The Coq formalization of the above operational semantics is direct: Reserved Notation l ~ s > c t. Inductive exec ( l : procs ) : state cmd option state Prop := exec_basic : s c0 s1, Cmd0. exec0 s c0 s1 l ~ s > basic c0 s1 exec_seq : s s1 s2 c d, l ~ s > c Some s1 l ~ s1 > d s2 l ~ s > c. ; d s2 exec_ifte_true : s s1 b c d, [ b ] b_ s l ~ s > c s1 l ~ s > If b Then c Else d s1 exec_ifte_false : s s1 b c d, ~ [ b ] b_ s l ~ s > d s1 l ~ s > If b Then c Else d s1 exec_while_true : s s1 s2 b c, [ b ] b_ s l ~ s > c Some s1 l ~ s1 > while b c s2 l ~ s > while b c s2 exec_while_false : s b c, ~ [ b ] b_ s l ~ s > while b c Some s exec_call : s s1 p c, Procs. MapsTo p c l l ~ s > c s1 l ~ s > call p s1 exec_call_err : s p, Procs. find p l = None l ~ s > call p None where l ~ s > c t := ( exec l s c t ). 4

1.4 Hoare Logic Since we are dealing with procedures, the Hoare logic takes, like the operational semantics, the procedure environment as a parameter. Moreover, it is indexed by the set of procedure specifications that we can assume to hold; this makes it possible in particular to handle recursive calls. The procedure specifications are essentially Hoare triples: Record spec := Spec { pre : assert ; callee : proc ; post : assert }. Sets of procedure specifications are represented by the type Ensembles.Ensemble from the Coq standard library. In summary, the Hoare logic is defined as a quaternary predicate of type Ensemble spec assert cmd assert Prop with parameter of type procs. The rules for sequence, conditional branching, and while-loops are textbook. hoare0 P c Q l \^ E ~{[ P ]} basic c {[ Q ]} hoare basic l \^ E ~{[ P ]} c {[ Q ]} l \^ E ~{[ Q ]} d {[ R ]} hoare seq l \^ E ~{[ P ]} c.; d {[ R ]} l \^ E ~{[ fun s => P s [ b ]b_s ]} c {[ P ]} l \^ E ~{[ P ]} while b c {[ fun s => P s ~[ b ]b_s ]} hoare while There are two rule for procedure calls. hoare call2 is for when the Hoare triple happens to be a specification from the environment. hoare call makes it possible to handle recursive calls: it extends the environment of procedure specifications E with new specifications E in which one may assume the conclusion Hoare triple. Spec P p Q E l \^ E ~{[ P ]} call p {[ Q ]} hoare call2 E, Spec P p Q E t, t E c, l callee t c l \^ E E ~{[ pre t ]} c {[ post t ]} l \^ E ~{[ P ]} call p {[ Q ]} hoare call The textbook consequence rule would be the following one: P ===> P l \^ E ~{[ P ]} c {[ Q ]} Q ===> Q l \^ E ~{[ P ]} c {[ Q ]} We actually need to generalize it so as to handle recursion. The following hoare conseq rule makes it possible to transport information about auxiliary variables from the pre-condition to the post-condition (see exercise): s, P s P Q, l \^ E ~{[ P ]} c {[ Q ]} P s Q ===> Q l \^ E ~{[ P ]} c {[ Q ]} hoare conseq 5

The encoding of the corresponding inductive predicate follows the penciland-paper description: Reserved Notation l \ˆ E ~ { [ P ] } c { [ Q ] }. Inductive hoare ( l : procs ) : Ensemble spec assert cmd assert Prop := hoare_basic : E P Q c, Cmd0. hoare0 P c Q l \ˆ E ~ { [ P ] } basic c { [ Q ] } hoare_seq : E P Q R c d, l \ˆ E ~ { [ P ] } c { [ Q ] } l \ˆ E ~ { [ Q ] } d { [ R ] } l \ˆ E ~ { [ P ] } c. ; d { [ R ] } hoare_conseq : E c ( P Q : assert ), ( s, P s P Q, l \ˆ E ~ { [ P ] } c { [ Q ] } P s ( Q = Q ) ) l \ˆ E ~ { [ P ] } c { [ Q ] } hoare_while : E P b c, l \ˆ E ~ { [ fun s P s [ b ] b_ s ] } c { [ P ] } l \ˆ E ~ { [ P ] } while b c { [ fun s P s ~ [ b ] b_ s ] } hoare_ifte : E P Q b c d, l \ˆ E ~ { [ fun s P s [ b ] b_ s ] } c { [ Q ] } l \ˆ E ~ { [ fun s P s ~ [ b ] b_ s ] } d { [ Q ] } l \ˆ E ~ { [ P ] } If b Then c Else d { [ Q ] } hoare_call : E P Q p E, In E ( Spec P p Q ) ( t, In E t c, Procs. MapsTo ( callee t ) c l l \ˆ Union E E ~ { [ pre t ] } c { [ post t ] } ) l \ˆ E ~ { [ P ] } call p { [ Q ] } hoare_call2 : E P Q p, In E ( Spec P p Q ) l \ˆ E ~ { [ P ] } call p { [ Q ] } where l \ˆ E ~ { [ P ] } c { [ Q ] } := ( hoare l E P c Q ). 1.5 Soundness of Hoare Logic We have to check that the proof system of Sect. 1.4 agrees with the operational semantics of Sect. 1.3. For that purpose, we define the meaning of Hoare triples using the operational semantics. We denote by l \^ E ={{ P }} c {{ Q }} this semantics of Hoare triples. It is defined as follows. First, the semantics without the environment of specifications: Definition hoare_sem l P c ( Q : assert ) : Prop := s, P s ~ l ~ s > c None ( s1, l ~ s > c Some s1 Q s1 ). Notation l ={{ P }} c {{ Q }} := ( hoare_sem l P c Q ). Second, we take care of all the specification in the environment: Definition hoare_sem_ctxt l E P c Q := ( t, In E t l ={{ pre t }} call ( callee t ) {{ post t }}) l ={{ P }} c {{ Q } }. Notation l \ˆ E ={{ P }} c {{ Q }} := ( hoare_sem_ctxt l E P c Q ) 6

Proving the soundness of Hoare logic amounts to prove the following lemma, which of course takes as hypothesis the soundness of not-yet-instantiated basic commands): Lemma hoare_sound E P Q l c : ( P Q l c, Cmd0. hoare0 P c Q l ={{ P }} basic c {{ Q }}) l \ˆ E ~ { [ P ] } c { [ Q ] } l \ˆ E ={{ P }} c {{ Q } }. The proof uses intermediate lemmas that require an operational semantics augmented with a natural n that counts the depth of procedure calls. See the companion Coq file for details. 2 Verification of Concrete Imperative Programs We now instantiate the generic Hoare logic of Sect. 1, and use it to verify imperative implementations of the factorial function. 2.1 Instantiation We instantiate states as maps from variables (represented by strings) to natural numbers: Module Vars := FMapList. Make ( String_as_OT ). Definition var := Vars. key.... Definition state := Vars. t nat. We provide a concrete arithmetic language exp: Inductive exp := exp_var : var exp add : exp exp exp mul : exp exp exp sub : exp exp exp cst : nat exp. In particular, a variable is denoted by % v. Addition (resp. multiplication and subtraction) are denoted by \+ (resp. \* and \-). The concrete boolean language bexp is built on top of exp: Inductive bexp := equa : exp exp bexp neg : bexp bexp. We note \= for boolean equality and \!= for inequality (i.e., a combination of neg and equa). Last, we provide a syntax, semantics, and Hoare triple for the assignment command. Assignment is denoted by v e where e is an expression and v a variable: Inductive cmd0 := assign : var exp cmd0. 7

The operational semantics reads as ~s >- v e ---> s{[ e ]_s/v}: Inductive exec0 : state cmd0 option state Prop := exec0_assign : s v e, exec0 s ( v e ) ( Some ( upd v ( [ e ] _s ) s ) ). The update of variables is implemented by updating the state by destructive addition: Definition upd v ( n : nat ) s := Vars. add v n s. The Hoare triple reads as \^ ~{[ Q{[ e ]_s/v} ]} v e {[ Q ]}. Inductive hoare0 : assert cmd0 assert Prop := hoare0_assign : Q v e, hoare0 ( wp_assign v e Q ) ( v e ) Q. The substitution is implemented by the predicate transformer wp_assign whose semantics is also defined using state update: Inductive wp_assign v e P : assert := wp_assign_c : s, P ( upd v ( [ e ] _s ) s ) wp_assign v e P s. We can now instantiate the functor of Sect. 1 with concrete states and basic commands: Module C := Cmd state cmd0. This provides types C.cmd and C.hoare that can be used to perform verification of While programs with parameterless procedures. We also have the soundness guarantee since the following holds (for the cmd0 type): Lemma sound0 P Q l c : cmd0. hoare0 P c Q l ={{ P }} C. basic c {{ Q } }. 2.2 Factorial as a While-loop The following C-like factorial program while (x!= 0) { ret = ret * x ; x = x - 1 } is written as follows using our Coq encoding: Definition facto := ( C. while (% x \ 0) ( r e t % r e t \ % x. ; x % x \ 1))% string. For specification, we use the Gallina function fact from the Factorial module of the standard library. If facto is started in a state where x has value X and ret has value 1, then it will end up in a state where ret has value fact X (X!): 8

Lemma facto_fact X : ( Procs. empty _ \ˆ @Empty_set _ ~ { [ fun s [% x ] _s = X [% r e t ] _s = 1 ] } facto { [ fun s [ % r e t ] _ s = fact X ]})% string. Here, the environment is empty (since there is no procedure call). The main idea of the proof is to use ret x = X! for the loop invariant. 2.3 Factorial as a Recursive Function We now verify another version of factorial written with a recursive call. In a C-like, informal syntax: void facto { if x == 0 { ret = 1 } else { x = x - 1 ; facto () x = x + 1; ret = ret * x } } Using our syntax: Definition facto : C. cmd := ( If (% x \= 0) Then r e t 1 Else x % x \ 1. ; C. call f a c t o. ; x % x \+ 1. ; r e t % r e t \ % x )%string. Verification of the recursive facto amounts to prove the following Hoare triple. It is similar to the one for the while-version except that the procedure environment contains the procedure body associated to the string facto : Lemma facto_fact X : ( Procs. add f a c t o facto ( Procs. empty _ ) \ˆ @Empty_set _ ~ { [ fun s [% x ] _s = X ] } C. call f a c t o { [ fun s [% x ] _s = X [ % r e t ] _ s = fact X ]})% string. 9

Proof Overview The proof starts by applying hoare call with the addition of x<x factospec x to the environment, where factospec is: Definition factospec x := ( C. Spec ( fun s [ % x ] _s = x ) f a c t o ( fun s [ % x ] _s = x [ % r e t ] _ s = fact x))% string. Then, when running into the recursive call, the Hoare triple should look like this: l \^ x<x factospec x ~ {[ x = n 1 1 n ]} call facto {[ x = n 1 ret = (n 1)! 1 n ]}. We need to adapt the pre/post-conditions so as to apply hoare call2. We use the hoare conseq rule for that purpose. See the companion Coq file for the sketch of formal proofs. References [1] Norbert Schirmer. Verification of Sequential Imperative Programs in Isabelle/HOL. Technische Universität München. 2006. 10