can c++ be made as safe as spark? david crocker, escher technologies

33
Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Upload: violet-barker

Post on 17-Dec-2015

217 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK?

David Crocker, Escher Technologies

Page 2: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 2

Motiviation• When developing software to very high integrity levels, testing is

insufficient to show the required integrity level has been reached, so formal verification is typically requires as well

• The SPARK tool set is probably the most-used formal verification system at the programming language level

• However, critical software is increasingly written in C and C++, especially in the automotive sector

• Can development in C or C++ provide similar levels of software integrity as development in SPARK?

Page 3: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 3

Bounded queue example• Our example is a bounded queue of characters with fixed capacity

• I will show it might be implemented in SPARK Ada

• I will show how to implement a C++ equivalent that, like the SPARK version:– Provides a clean interface and hides the data– Has a formal proof of correctness

Page 4: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

-- File BoundedQueue.ads

package BoundedQueue with SPARK_Mode is capacity: constant Integer := 64; type Queue is private;

function empty(q: in Queue) return Boolean;

function full(q: in Queue) return Boolean;

procedure add(q: in out Queue; val: in Character) with Pre => not full(q) ;

procedure remove(q: in out Queue; val: out Character) with Pre => not empty(q) ;

procedure init(q: out Queue) with Post => empty(q);

private subtype StorageIndex is Integer range 0..capacity;

type Storage is array (StorageIndex) of Character;

type Queue is record data: Storage; nextIn: StorageIndex; nextOut: StorageIndex; end record;

end BoundedQueue;

The package specification

Page 5: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

// File BoundedQueue.h

#define CAPACITY (64u)

typedef struct { char data[CAPACITY + 1u]; unsigned int nextIn; // ranges from 0..CAPACITY unsigned int nextOut; // ranges from 0..CAPACITY} Queue;

bool empty(const Queue *q);

bool full(const Queue *q);

void add(Queue *q, char c); // on entry the queue must not be full

char remove(Queue *q); // on entry the queue must not be empty

void init(Queue *q); // on return the queue is empty

Let’s try that in C…

Page 6: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Problem: lack of encapsulation• The SPARK version declares Queue to be a private record type

• In the C version, the Queue structure is exposed– which means that clients could reads/write its field directly

• C has no adequate mechanism for data hiding

• Can we do better with C++?

type Queue is private; … private… type Queue is record data: Storage; nextIn: StorageIndex; nextOut: StorageIndex; end record;

typedef struct { char data[CAPACITY + 1u]; unsigned int nextIn; unsigned int nextOut;} Queue;

Page 7: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

// File BoundedQueue.hpp

const unsigned int capacity = 64u;

class Queue {public: bool empty() const;

bool full() const;

void add(char c); // on entry the queue must not be full

char remove(); // on entry the queue must not be empty

Queue(); // on return the queue is empty

private: char data[capacity + 1u]; unsigned int nextIn; // ranges from 0..CAPACITY unsigned int nextOut; // ranges from 0..CAPACITY};

Let’s try it in C++

Page 8: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 8

SPARK and C++ versions compared

• Both keep the data private

• Both provide empty and full functions, add and remove procedures

• Parameter passing:– SPARK used in and out keywords to indicate direction– C++ uses const and absence of const to indicate whether a parameter

passed by pointer is changed or not

• How to initialize a Queue– In the SPARK version, you call the init procedure– In the C++ version, the default constructor will be called automatically

• But the C++ version is still missing something…

Page 9: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 9

Function contracts!• The SPARK version provides function contracts:

• How to do this in C++?

• Maybe do it the way SPARK used to, in comments?

procedure add(q: in out Queue; val: in Character) with Pre => not full(q) ;

procedure remove(q: in out Queue; val: out Character) with Pre => not empty(q) ;

procedure init(q: out Queue) with Post => empty(q);

Page 10: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 10

const unsigned int capacity = 64u;

class Queue {public: bool empty() const;

bool full() const;

void add(char c); //# pre !full()

char remove(); //# pre !empty()

Queue(); //# post empty()

private: char data[capacity + 1u]; unsigned int nextIn; // ranges from 0..CAPACITY unsigned int nextOut; // ranges from 0..CAPACITY};

Adding function contracts

Page 11: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 11

A nicer way of adding function contracts

• C++ provides a preprocessor

• The preprocessor can be used to define and expand macros

• So how about using macros:

• When the file is compiled, the compiler expands the pre(…) part to nothing, so it gets ignored

• Text editors do syntax highlighting on macro calls, just as for code

#define pre(expression) // nothing

void add(char c) pre(!full());

Page 12: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 12

#include <ecv.h> // for specification macro definitions

const unsigned int capacity = 64u;

class Queue {public: bool empty() const;

bool full() const;

void add(char c) pre(!full());

char remove() pre(!empty());

Queue() post(empty());

private: char data[capacity + 1u]; unsigned int nextIn; // ranges from 0..CAPACITY unsigned int nextOut; // ranges from 0..CAPACITY};

Adding function contracts

Page 13: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 13

What’s still missing from the C++ version?

• The SPARK version uses range-constrained types:

• It’s not essential to use range-constrained types in this example, but they can help with verification– e.g. by detecting out-of-range values earlier

• Why not add range-constrained types to C++?

subtype StorageIndex is Integer range 0..capacity;

type Storage is array (StorageIndex) of Character;

type Queue is record data: Storage; nextIn: StorageIndex; nextOut: StorageIndex; end record;

Page 14: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 14

Adding range-constrained types to C++

• We could use a class template:

• If we don’t need run-time checking, we can use annotations instead

class ConstrainedInt<int minVal, int maxVal> {public: operator int() const { return val; }

ConstrainedInt(int arg) pre(arg >= minVal; arg <= maxVal) { if (arg < minVal || arg > maxVal) { throw ConstraintError(arg); } val = arg; }

private: int val;}

ConstrainedInt<0, capacity> nextIn, nextOut;

Page 15: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 15

Extending the C++ typedef declaration

• C and C++ allow you to define synonyms for types:

• Let’s add constraints to typedef declarations:

• We also add the rule that a pointer to a constrained type is not assignment-compatible with a pointer to any other type, even if the constraint is “true”

typedef size_t StorageIndex;

#define invariant(expression) // nothing

typedef size_t invariant(value <= capacity) StorageIndex;

Page 16: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

#include <ecv.h> // for specification macro definitions

const unsigned int capacity = 64u;

class Queue {public: bool empty() const;

bool full() const;

void add(char c) pre(!full());

char remove() pre(!empty());

Queue() post(empty());

private: typedef unsigned int invariant(value <= capacity) StorageIndex;

char data[capacity + 1u]; StorageIndex nextIn; StorageIndex nextOut;};

Using range-constrained types

Page 17: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 17

What about the body?

Page 18: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

-- File BoundedQueue.adb

package body BoundedQueue with SPARK_Mode is function empty(q: in Queue) return Boolean is begin return q.nextIn = q.nextOut end;

function full(q: in Queue) return Boolean is begin return (q.nextIn + 1) mod (capacity + 1) = q.nextOut; end;

procedure add(q: in out Queue; val: in Character) is begin q.data(q.nextIn) := val; q.nextIn := (q.nextIn + 1) mod (capacity + 1); end;

procedure remove(q: in out Queue; val: out Character) is begin val := q.data(q.nextOut); q.nextOut := (q.nextOut + 1) mod (capacity + 1); end;

procedure init(q: out Queue) is begin q.nextIn := 0; q.nextOut := 0; q.data := (others => Character'Val(0)); -- for SPARK end;

end BoundedQueue;

Page 19: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

// File BoundedQueue.cpp

#include "BoundedQueue.hpp“#include <cstring> // for memset()

bool Queue::empty() const { return nextIn == nextOut;}

bool Queue::full() const { return (nextIn + 1u) % (capacity + 1u) == nextOut;}

void Queue::add(char c) { data[nextIn] = c; nextIn = (nextIn + 1u) % (capacity + 1u);}

char Queue::remove() { char temp = data[nextOut]; nextOut = (nextOut + 1u) % (capacity + 1u); return temp;}

Queue::Queue() { nextOut = 0; nextIn = 0; memset(data, 0, sizeof(data));}

Page 20: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 20

Verification in SPARK 2012 GPL edition

Page 21: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 21

Verification of the C++ version

…and so on until…

Page 22: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 22

Similarities and differences• SPARK reports 5 VCs generated, 4 proved

– It appears to hide some “trivial” VCs, e.g. provable range checks

• Our tool for C++ reports 37 VCs generated, 36 proved– The number of VCs doesn’t depend on whether they succeed or not

• Neither can prove that the init function or constructor yields an empty queue– Because we haven’t provided a specification for empty()

• We have proved that the program is valid in one sense– “Exception freedom” for the Ada version– “Absence of undefined or unspecified behaviour” for the C++ version

• But we haven’t proved that it behaves like a queue!

Page 23: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 23

How should a queue behave?• Logically, a queue is a sequence of elements

– We add elements to one end and remove them from the other

• So we should specify the queue operations in terms of a sequence

• This calls for data refinement– The abstract data is a sequence with varying numbers of elements– The concrete data is a fixed length array and two indices into it– A retrieve relation defines the relationship between abstract and concrete

data

Remove elements from head Add elements to tail

Page 24: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 24

Expressing data refinement in C++

• We allow ghost functions to be declared– A ghost function is for use in specifications only

• To express the retrieve relation, we declare a ghost function that returns the abstract data– Then we can write specifications in terms of calls to that function

• For the type of the abstract data, we can use _ecv_seq<char>– This is a built-in ghost type that represents a sequence– It supports the usual sequence operations including count(), head(), tail(),

take(n), drop(n), append(c) and concat(s)– It supports quantification over the elements and a few higher order

functions (filter, map, left-fold, ...)

Page 25: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Retrieve function for a circular buffer

• Otherwise, we want the elements from nextOut to the end of the buffer, followed by elements from the start of the buffer up to nextIn:

nextOut nextIn

nextOutnextIn

data

data

data.take(nextIn)

data.take(nextIn) data.drop(nextOut)

• If nextIn >= nextOut, we want the elements in between them:

Queue contents are:data.drop(nextOut) .concat(data.take(nextIn))

Queue contents are:data.take(nextIn) .drop(nextOut)

Page 26: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

const unsigned int capacity = 64u;

class Queue {public: // Retrieve function ghost( ecv_seq<char> contents() const

returns( (nextIn >= nextOut) ? data.take(nextIn).drop(nextOut) : data.drop(nextOut).concat(data.take(nextIn))

); )

bool empty() const returns(contents().count == 0);

bool full() const returns(contents().count == capacity);

void add(char c) pre(!full()) post(contents() == (old contents()).append(c));

char remove() pre(!empty()) returns(contents().head()) post(contents() == (old contents()).tail());

Queue() post(empty());

private: …

Page 27: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 27

Verification with data refinement

…and so on…

Page 28: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies
Page 29: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 29

Also in the paper…• Weaknesses in the C++ language have to be mitigated

– We apply most MISRA-C:2008 rules– We use annotations to strengthen the type system in respect of pointers– We only support those C++ constructs that we believe are safe to use

and have formalised– We add further safety rules, e.g. to restrict calls to overloaded functions

• Single inheritance with dynamic binding– We prove subtype compatibility (Liskov Substitution Principle) as required

by DO-332 objective OO-6.7.2

• C++ template declarations– Generic version of the bounded queue example

Page 30: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 30

Future work (1)

• Template instantiation preconditions– Declare the auxiliary operators etc. needed to instantiate a template

• Semantics of different sorts of volatile variables– Currently, we treat all volatile variables as subject to unpredictable

changes in value, so they can’t be used in specifications– But not all volatile variables can change unpredictably at all times – SPARK 2014 uses the concept of external state to handle this

template<class X> void sort(Array<X<> table) require bool operator<(X, X) post(…){ … }

Page 31: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 31

Future work (2)• Concurrency

– C++ 2011 has a concurrency model– Shared-variable concurrency in general is a difficult problem– Microsoft’s Vcc handles it to some degree, but the annotation is hard– Taming Concurrency project (Cliff Jones, Newcastle)

• Floating point arithmetic– If we model FP arithmetic as real arithmetic, we can do useful things, but

can also produce false proofs, e.g. 3.0 * (1.0/3.0) == 1.0– If we model FP arithmetic more accurately, a lot of “useful” things become

unprovable– In simple cases, range arithmetic may be suitable

Page 32: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 32

Related work• Larch/C++ project

– Defined an annotation language, but not supported by verification tools

• Several formal verification systems for C– Frama/Jessie, Vcc, Verifast, and our own eCv

Page 33: Can C++ be made as safe as SPARK? David Crocker, Escher Technologies

Can C++ be made as safe as SPARK? 33

Conclusion• By adding selected C++ features to MISRA-C:2012 we have defined

a subset of C++ that we believe is suitable for high-integrity software– and offers substantial advantages over C

• Programs written in this subset can be verified formally in the same way as programs written in the SPARK subset of Ada

• Applications of the tool so far:– SIL 4 software in the defence industry – Medical equipment (joint work with Newcastle University)

• Questions?