0% found this document useful (0 votes)
51 views13 pages

C: A Verification Framework For Concurrent Imperative Programs

1

Uploaded by

Fahmi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
51 views13 pages

C: A Verification Framework For Concurrent Imperative Programs

1

Uploaded by

Fahmi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

C OMPLX: A Verification Framework

for Concurrent Imperative Programs

Sidney Amani1 June Andronick1,2 Maksym Bortin1


1
Corey Lewis Christine Rizkallah3,∗ Joseph Tuong4,∗
1 2 3 4
Data61, CSIRO, Australia UNSW, Australia University of Pennsylvania, U.S. Freelancer, Australia
1 3 4
[email protected] [email protected] [email protected]

Work done while at Data61, CSIRO

Abstract performance-demanding low-level systems, such as oper-


We propose a concurrency reasoning framework for im- ating system (OS) kernels or real-time systems, also have
perative programs, based on the Owicki-Gries (OG) foun- strong safety and security objectives, which call for for-
dational shared-variable concurrency method. Our frame- mal verification. Multiple frameworks for formal reasoning
work combines the approaches of Hoare-Parallel, a formal- about C programs exist and have successfully been used,
isation of OG in Isabelle/HOL for a simple while-language, ranging from push-button automated tools to check the ab-
and S IMPL, a generic imperative language embedded in Is- sence of classes of runtime errors, to more effort-intensive
abelle/HOL, allowing formal reasoning on C programs. interactive methods to prove deeper correctness properties.
We define the C OMPLX language, extending the syntax We target the latter, minimising the trust needed in the tools,
and semantics of S IMPL with support for parallel compo- maximising the strength of properties that can be proven. To
sition and synchronisation. We additionally define an OG this end, we propose a framework for formal, interactive ver-
logic, which we prove sound w.r.t. the semantics, and a ver- ification of shared-variable concurrent imperative low-level
ification condition generator, both supporting involved low- programs, which can be combined with a C parser front end
level imperative constructs such as function calls and abrupt for concurrent C code verification, paving the way to verified
termination. We illustrate our framework on an example that interruptible or multicore systems.
features exceptions, guards and function calls. We aim to We follow a common approach to reasoning about pro-
then target concurrent operating systems, such as the inter- grams: embedding the given language within a powerful the-
ruptible eChronos embedded operating system for which we orem prover, Isabelle/HOL [Nipkow et al. 2002] in our case.
already have a model-level OG proof using Hoare-Parallel. That is, we define abstract and concrete syntax, and specify
runtime behaviour. Although it is possible to directly reason
Categories and Subject Descriptors D.1.3 [Programming over the semantics of programs, it is untenable and not scal-
Techniques]: Concurrent Programming; F.3.1 [Logics and able. It can instead be automated by the definition of a set of
Meanings of Programs]: Specifying and Verifying and Rea- logic rules, reducing the program’s correctness statement to
soning about Programs a series of simpler verification conditions. This verification
Keywords formal verification, programming languages, condition generator (VCG) is typically syntax-directed, un-
imperative code, concurrency, Owicki-Gries, Isabelle/HOL folding the proof according to the rules of the logic. In order
to justify such reasoning with our set of rules, we prove their
1. Introduction soundness with respect to the language’s formal semantics.
Such an infrastructure exists for reasoning about sequen-
C is still the language of choice for developing software with
tial C programs in Isabelle/HOL. C programs are translated
high performance and precise memory requirements, for it
into S IMPL [Schirmer 2006, 2008], a generic, sequential,
allows aggressive manual optimisation. At the same time,
imperative language formalised in Isabelle/HOL. The C-to-
Permission to make digital or hard copies of all or part of this work for personal or Isabelle translation [Tuch et al. 2007] is unavoidably trusted,
classroom use is granted without fee provided that copies are not made or distributed
for profit or commercial advantage and that copies bear this notice and the full citation parsing C code into formal logic, and is therefore as con-
on the first page. Copyrights for components of this work owned by others than ACM servative and direct as possible. The S IMPL framework pro-
must be honored. Abstracting with credit is permitted. To copy otherwise, or republish,
to post on servers or to redistribute to lists, requires prior specific permission and/or a vides syntax and semantics for the language, as well as a
fee. Request permissions from [email protected].
Hoare logic (with its soundness proof) and a VCG. It has
CPP’17, January 16–17, 2017, Paris, France
c 2017 ACM. 978-1-4503-4705-1/17/01...$15.00 successfully been used in the landmark verification of the
http://dx.doi.org/10.1145/3018610.3018627 seL4 microkernel, guaranteeing multiple correctness prop-

138
erties of seL4 to the C implementation [Klein et al. 2010; and potentially a multicore variant of seL4. Our framework
Murray et al. 2012]. The framework, however, lacks the abil- assumes that the granularity of interleaving is that of C
ity to reason about concurrency. instructions; porting the guarantees down to executable code
We extend S IMPL with support for shared-variable con- and weak memory architectures is not in the scope of this
currency, following the foundational Owicki-Gries (OG) paper.
method [Owicki and Gries 1976]. OG has been for- All our Isabelle/HOL formalisations and the case studies
malised in Isabelle/HOL’s library in the Hoare-Parallel the- are available online [COMPLX].
ory [Prensa Nieto 2002] for a simple high-level while-
language IMP. We chose OG over more recent variants 2. Background
(e.g. Rely-Guarantee [Jones 1983], Concurrent Separation In this section, we present existing work that our paper
Logic [OHearn 2007]) for the simplicity of OG logic combines and extends. The first section presents S IMPL,
and its suitability to reason about potentially-racy high- an existing formalisation of sequential imperative programs
performance shared-variable system code: we previously in Isabelle/HOL (and the existing infrastructure to verify
successfully used it for a model-level verification of the in- C code). The second presents Hoare-Parallel, an existing
terruptible eChronos embedded OS [Andronick et al. 2015, formalisation of OG for a simple while-language in Is-
2016]. The OS provides an API to applications for synchro- abelle/HOL. Our work consolidates those two components
nisation and locking, but the OS code itself shares racy mem- to create a language that provides a basis for reasoning about
ory state with interrupt handlers1 . In this previous work, we concurrent C code in Isabelle/HOL.
used Hoare-Parallel’s formalisation of OG and we demon-
strated how the well-known explosion of verification con- 2.1 Verification of C in Isabelle/HOL
ditions of the OG method can be efficiently handled by the As mentioned in the introduction, S IMPL allows embedding
powerful automation of modern theorem provers and by the of real programming languages into Isabelle/HOL, and is
careful modelling of controlled interleaving. We now want sufficiently expressive to model a substantial subset of C
to push our proofs down to guarantees about the C imple- features. S IMPL can be used directly for reasoning about
mentation, which is the motivation for the work presented C code and it has indeed been used directly in the verifi-
here. cation of LEDA’s [Mehlhorn and Näher 1999] shortest path
Our contributions are the following: checker [Rizkallah 2014].
• We propose the language C OMPLX as an extension of A far more common verification approach though is using
S IMPL with support for parallel composition and syn- the C-to-Isabelle parser [Tuch et al. 2007] which converts a
chronisation, and we define its concurrent semantics. We large subset of C99 code into low-level S IMPL code. S IMPL
largely reuse S IMPL’s existing infrastructure to facilitate and the C-to-Isabelle parser together provide an established
the port of existing verifications that use S IMPL (sec- infrastructure for the verification of sequential C programs in
tion 3). Isabelle/HOL. They have been used in the verification of the
seL4 microkernel which is written in C [Klein et al. 2010]
• We define a practical OG-based logic, inspired by Hoare-
and in several other C verification projects [Amani et al.
Parallel, and a VCG to facilitate semi-automated proof 2016; Murray et al. 2012; Noschinski et al. 2014].
using the logic (section 4).
• We prove our logic sound with respect to the semantics,
Syntax S IMPL provides the usual imperative language
constructs, including functions, variable assignment, se-
ensuring that proofs using the logic are true guarantees
quential composition, conditional statements, while loops,
about the execution of the program (section 5).
and exceptions. S IMPL has no expression language of its
• Finally, we present a case-study demonstrating the use own; expressions are shallowly embedded. The notion of
and practicality of this framework for the verification of state is also generic and left for instantiation; it is defined as
concurrent imperative programs (section 6). an Isabelle record of local and global variables (variables are
• As part of the examples, we demonstrate how we support then simply functions on the state). The C-to-Isabelle parser
concurrent function calls, including a technique to handle only supports side-effect-free expressions, modelled as Is-
arguments passing and local variables. abelle/HOL expressions, and it instantiates the state space
to C memory states. The following is a summary of S IMPL
With additional work, the existing infrastructure around syntactic forms, where e represents an expression:
S IMPL, including the C-to-Isabelle parser, can be updated to
enable reasoning about a significant subset of concurrent C c = Skip | v := e | c1 ; ; c2 | IF e THEN c1 ELSE c2 FI
code in Isabelle/HOL. This would open up the applicability | WHILE e DO c OD | TRY c1 CATCH c2 END
to several existing codebases, including the eChronos OS, | Throw | Call n | DynCom cs | Guard f g c
1 Preventing races would require disabling interrupts, resulting in increases The DynCom cs statement is a dynamic (state depen-
of latency unacceptable for such real-time systems. dent) command that takes as argument cs which is a func-

139
tion from states to commands. It is used in C verification to 2.2 Verification of Concurrent Code in Isabelle/HOL
encode argument passing and scoping in function calls. The Hoare logic may be used to prove that a thread in a concur-
Guard f g c statement throws the fault f if the condition g rent program is locally correct, i.e. that it is correct under a
is false and executes c otherwise. It is used in C verifica- sequential interpretation of its semantics without interleav-
tion to encode certain correctness conditions ensuring that ing of external commands. In order to prove that it is correct
the C program does not exhibit undefined behaviour (e.g. in a concurrent setting, we have to additionally prove that it
division by zero). Call just takes the name of the function is globally correct, i.e. that is is still correct considering all
being called. possible interleavings with other threads in the system.
The Owicki-Gries [Owicki and Gries 1976] method for
Semantics Computations in S IMPL are described by sev- the verification of shared-variable concurrent programs ex-
eral equivalent models, including big- and small-step seman- tends the proof method for sequential correctness with the
tics. Here we are interested in the small-step semantics, as concept of interference freedom: each thread is first proved
we want to model the fine-grain interleaving of concurrent to be locally correct and then each atomic command in each
programs. thread is proved to not interfere with (i.e. invalidate) the local
The small-step semantics is represented by statements of correctness proof of another thread in parallel. If the proof of
the form Γ ` hc, si → hc0 , s0 i that read as: program c in state local correctness for a command c requires a pre-condition
s takes a step to program c0 and the updated state s0 under P , and c may be interleaved with another command c0 whose
the procedure environment Γ which maps function names pre-condition is P 0 , then in order to show interference free-
to function bodies. Both s and s0 are extended states: they dom, we show that {P 0 ∧P } c0 {P } holds, i.e. that P remains
are either Normal states, representing typical execution flow true after being interleaved with c0 .
(including exception handling), or Stuck states, generated In order to satisfy the requirements for interference free-
by calls to non-existent procedures, or Fault states, gener- dom over all threads in a system, it is necessary to store these
ated by failed Guard statements. For normal program states, intermediate assertions. Unlike for a sequential program, we
s = Normal x, the semantics is as expected; whereas in may require post-conditions to be arbitrarily stronger than
cases s = Stuck or s = Fault f we may only transform c to the weakest pre-condition implied by the Hoare logic. For
Skip with s0 = s. this reason, we need to fully annotate the concurrent pro-
Exceptions are used to represent abrupt termination — gram with intermediate assertions in order to verify its cor-
function calls and loops are wrapped in a try-catch block and rectness relative to other threads in the system, in contrast to
the C statements return, break, and continue are imple- a sequential program, for which these properties may largely
mented by assigning appropriate value to an auxiliary vari- be automatically derived and discarded once used.
able and raising an exception with Throw . The exception is Hoare-Parallel [Prensa Nieto 2002] is a formal reason-
caught by CATCH , mimicking an abrupt termination of the ing framework in Isabelle/HOL for a simple concurrent
TRY block. language, including a formalisation of OG. The language
consists of assignment, sequential composition, condition-
Verification Specifications for S IMPL programs are given als, loops, and two additional statements for concurrency:
as Hoare triples, where pre-conditions and post-conditions Parallel [ac1 ..acn ] and Await b c. The execution is mod-
are stated by Isabelle expressions. The S IMPL environment elled through a small-step semantics; an await statement can
provides a VCG for partial correctness that converts those only do a step if its boolean guard b is true, in which case
Hoare triples to a set of higher-order formulas that are eas- its body c is executed atomically; a step of a parallel com-
ier to reason about. The Hoare triples are represented by position of programs is a step of any of its thread that is not
statements of the form Γ `/F P c Q, A, where P is the blocked on an await. Hoare-Parallel’s abstract syntax is de-
pre-condition, Q is the post-condition for normal termina- fined using the following mutually recursive datatype:
tion, A is the abrupt-condition for abrupt termination2 , and
F is the set of faults allowed. A soundness proof guarantees
the safe use of the Hoare logic instead of directly reasoning ac = AnnSeq ac ac | AnnBasic r f | AnnCond b ac ac
about the semantics: it states that if such a Hoare triple is | AnnWhile r b r ac | AnnAwait r b c
established, then all final states reached through the execu- and
tion of the command (according to the semantics) from an c= Parallel [ac..ac] | Seq c c | Basic f | Cond b c c
initial state that satisfies the pre-condition, will satisfy the | While b r c
post-condition and the abrupt-condition.
The outer layer c performs sequential actions or initiates
2 Using Schirmer’s [Schirmer 2006] terminology, we refer to post-
a parallel composition, and the inner layer ac within the
conditions for abrupt-termination due to uncaught exceptions as abrupt- parallel composition expresses a thread, with each action
conditions. annotated with an assertion r.

140
COBEGIN . . . COEND is used as syntactic sugar uations arise that neither S IMPL nor the Hoare-Parallel for-
for Parallel . Throughout the paper we reuse the Hoare- malisations had to deal with. For instance, we have to decide
Parallel’s Parallel concrete syntax for C OMPLX. how a parallel program shall behave in the case when one
of its threads raises an uncaught exception. In this case we
3. C OMPLX: Syntax and Semantics allow the parallel program to stop all other threads and exit
Recall that the aim of C OMPLX is to enrich S IMPL with par- with the exception. The rule Parallel-Throw:
allel composition and synchronisation. The Hoare-Parallel Throw ∈ set cs
development shows how the OG method can be formalised Γ ` hParallel cs, si → hThrow , si
in Isabelle/HOL, introducing syntax and the small-step se-
mantics for parallel components. We largely reuse this ap- captures this behaviour, where set just converts a list to a set.
proach with two major deviations. Firstly, we do not incor- However, the parallel program may also continue its compu-
porate annotations into C OMPLX abstract syntax but rather tation, delaying the exception, provided by the fundamental
represent annotations using a separate datatype. Annotations Parallel rule:
and programs will be related in the next section by means of Γ ` hcsi , si → hc, s0 i i < |cs|
the OG logic. This way the abstract syntax remains simple
Γ ` hParallel cs, si → hParallel cs[i := c], s0 i
and clear, and we can reuse the existing C-to-Isabelle parser.
Secondly, we do not separate parallel and sequential pro- where |cs| denotes the length of the list cs, csi the i-th
grams into different layers, but rather have one datatype rep- (counting from 0) element of cs, and cs[i := c] the list cs
resenting both. These decisions make the soundness proof with its i-th element replaced by c. Furthermore, a paral-
more complicated, but allow C OMPLX programs to have lel program is allowed to terminate properly only if all its
nested parallelism, thus lifting unnecessary syntactic restric- threads do so. This is described by the Parallel-Skip rule:
tions.
In this sense, C OMPLX just extends the S IMPL abstract ∀c ∈ set cs. c = Skip
syntax by two new constructors: Parallel cs and Await b c: Γ ` hParallel cs, si → hSkip, si

c = Skip | . . . | Parallel cs | Await b c Next, for Await b c to be processed in a state Normal x,


the await-condition must be satisfied, i.e. x ∈ b must
where Parallel takes a list of programs cs that run in par- hold. Otherwise the execution is blocked. Moreover, the
allel, and Await takes a set of states b specifying the await- body of the await c must be a sequential program with-
condition, and a program c representing the await-body. It is out any further Await statements or Parallel composi-
worth noting that with nested parallelism we could use the tions. Following the Hoare-Parallel notation, we denote this
canonical binary parallel composition operator p k q instead condition by atom com c. Now, any computation Γ `
of Parallel cs without any effect to semantic expressivity, hc, Normal xi →∗ hSkip, Normal yi allows us to derive
since Parallel cs can be represented by folding the binary Γ ` hAwait b c, Normal xi → hSkip, Normal yi. In other
operator. On the other hand, an OG-rule for p k q would words, if the await-body terminates in a number of small-
lack the possibility to collect and handle interference free- steps without any interleavings then Await b c can make the
dom of more than two parallel components within a single same transition in a single step. Here again we have to con-
proof obligation, but distribute it in accordance to the fold sider potential exceptions raised by c, in which case we let
strategy. To avoid such complications, Parallel takes a list Await b c throw an exception as well. These behaviours are
of parallel components directly in C OMPLX abstract syntax formalised by the following rules, where s = Normal x and
as shown above. s0 = Normal y.
Next, we extend the small-step semantics of S IMPL to the
x ∈ b atom com c Γ ` hc, si →∗ hSkip, s0 i
new language constructs. As mentioned previously, we use
small-step semantics to allow for reasoning about interleav- Γ ` hAwait b c, si → hSkip, s0 i
ings between each atomic step. In what follows, we reuse x ∈ b atom com c Γ ` hc, si →∗ hThrow , s0 i
the S IMPL notation Γ ` hc, si → hc0 , s0 i meaning that the
Γ ` hAwait b c, si → hThrow , s0 i
configuration hc, si, comprising a C OMPLX program c and
a state s, can be transformed in one step to the configura- The cases when an execution of the await-body c results not
tion hc0 , s0 i under the procedure environment Γ. As usual, in Normal y, but in a state s0 other than normal (e.g. Stuck ),
we write Γ ` hc, si →∗ hc0 , s0 i for the reflexive-transitive are handled in a similar manner: hAwait b c, Normal xi can
closure of the small-step relation. take a single small-step to hSkip, s0 i.
To adapt the semantics of Parallel cs and Await b c from
Hoare-Parallel to S IMPL’s involved computation model, we 4. Owicki-Gries Logic for C OMPLX
have to take into account several kinds of states: Normal , Verification of programs by directly reasoning about the
Fault, and Stuck , as well as exception handling. New sit- semantics of the language is cumbersome and not easily

141
amenable to automation. For sequential S IMPL programs, For instance, the following is a C OMPLX program with
S IMPL’s Hoare logic allows for weakest pre-condition style the annotations and program text combined.
reasoning, generating intermediate assertions, and a small
x := 0;; y := 0;;
set of verification conditions that guarantee partial correct- COBEGIN
ness (i.e. correctness in case of termination). For our con- {|a|} x := 1 {|Qx |}, {|Ax |} k {|b|} y := 1 {|Qy |}, {|Ay |}
current C OMPLX programs, we create an OG logic, similar COEND
to the one defined in Hoare-Parallel, that breaks down the
correctness of a parallel program into local correctness and This produces two different trees, one for the program
global correctness verification conditions. itself (where Basic f models state update by the function
f , here variable assignment):
4.1 Annotations
As explained in section 3, we use a single datatype to repre- Seq (Basic (x update (λ-. 0)))
sent sequential and concurrent programs. Moreover, our OG (Seq (Basic (y update (λ-. 0)))
annotations are specified using a separate datatype called an (Parallel [Basic (x update (λ-. 1)),
annotation tree, which is isomorphic to the abstract syntax Basic (y update (λ-. 1))]))
tree of the C OMPLX program. The annotation tree contains
assertions at each step in the program and is represented as and a separate annotation tree of the form
follows:
AnnComp (AnnExpr {|True|})
a= AnnExpr r | AnnRec r a | AnnWhile r r a
(AnnComp (AnnExpr {|True|})
| AnnComp a a | AnnCond r a a
(AnnPar [(AnnExpr {|a|}, {|Qx |}, {|Ax |}),
| AnnPar l | AnnCall r i
(AnnExpr {|b|}, {|Qy |}, {|Ay |})]))
Non-recursive command constructors such as Skip, Throw ,
etc. are annotated via an AnnExpr node, which carries a In the annotation tree, the trivial, unused assertions for
single assertion r that is merely a set of states and is also the sequential parts are automatically added by the syntactic
used for post-conditions of OG rules. AnnRec is used to sugar, removing the burden from the user.
annotate recursive commands, such as Await, DynCom or
Guard s, that hold another annotated command a. While- 4.2 Owicki-Gries Rules
commands require a special annotation type that provides We define an OG statement of the form
an assertion for the while, a loop invariant, as well as an
annotation tree for the loop body. Sequential composition Γ, Θ `/F a c {|Q|}, {|A|}
and Catch statements are annotated via AnnComp, where
stating that the C OMPLX program c with the annotation tree
an annotation sub-tree is provided for each component of the
a either ends in one of the fault states specified by F , or a
sub-commands. Similarly, AnnCond is used for conditional
Normal state. If that Normal state is an exception, it must
statements, but in addition to the two annotation sub-trees, it
satisfy the abrupt-condition A, otherwise it must satisfy the
carries an assertion for the conditional statement itself.
post-condition Q. Γ is the procedure environment, mapping
AnnPar is used to annotate Parallel statements, hence,
function names to function bodies, and Θ is the annotation
it stores a list l of triples containing an annotation tree, a
environment, mapping function names to annotation trees.
post-condition and an abrupt-condition, with one element
To enable weakest pre-condition reasoning when proving
in the list per parallel component. The post-conditions and
a sequential part of a program (i.e. within an Await or top-
abrupt-conditions must be specified by the user, because
level non-parallel commands), we have another OG state-
they are part of the interference freedom requirements. More
ment which takes an extra pre-condition {|P |}:
specifically, we must show that none of these conditions can
be violated due to other components activity. Γ, Θ `
`/F {|P|} a c {|Q|}, {|A|}
Finally, Call statements are annotated with AnnCall ,
which holds an assertion r and a routine index i of type This means that we duplicate every OG rules and the
natural number, specifying which annotation tree to select sequential version of a rule ignores the annotation tree. We
from the annotation environment. We return to this at the borrowed this idea from Hoare-Parallel, which also has two
end of subsection 4.2. versions for each rule. In our case, the annotation tree exists
Despite having a separate datatype for the program and but is only used as soon as we switch to parallel mode.
the annotation tree, C OMPLX’s syntactic sugar allows a user Figure 1 illustrates some of the important OG logic rules
to annotate a program directly. This way we specify asser- for C OMPLX. We omitted all the rules used for sequential
tions at each step of the program, making it easy to keep reasoning except for SeqParallel which allows switching
track of the assertions when following the control flow of from sequential mode (denoted by ) to parallel mode (de-
the program. noted by `). This rule would be used when the program is

142
P ⊆ pre (AnnPar as) Γ, Θ `/F (AnnPar as) (Parallel cs) {|Q|}, {|A|}
S EQ PARALLEL
Γ, Θ `
`/F P (AnnPar as) (Parallel cs) {|Q|}, {|A|}
|as| = |cs| ∀ i<|cs|. Γ, Θ `/F (pres as[i] ) cs[i] (postcond as[i] ), (abrcond as[i] )
\ [
interfree Γ Θ F as cs map postcond as ⊆ {|Q|} map abrcond as ⊆ {|A|}
PARALLEL
Γ, Θ `/F (AnnPar as) (Parallel cs) {|Q|}, {|A|}
Γ, Θ `
`/F (r ∩ b) P c {|Q|}, {|A|} atom-com c r ⊆ pre a ∀ s∈r. Γ, Θ `/F a (d s) {|Q|}, {|A|}
AWAIT DYN C OM
Γ, Θ `/F (AnnRec r P) (Await b c) {|Q|}, {|A|} Γ, Θ `/F (AnnRec r a) (DynCom d) {|Q|}, {|A|}
Γ, Θ `/F P c {|Q|}, {|A|} r ∩ g ⊆ pre P r ∩ − g 6= ∅ −→ f ∈ F
G UARD
Γ, Θ `/F (AnnRec r P) (Guard f g c) {|Q|}, {|A|}
Θ p = Some as r ⊆ pre as[n] Γ p = Some b n < |as| Γ, Θ `/F as[n] b {|Q|}, {|A|}
C ALL
Γ, Θ `/F (AnnCall r n) (Call p) {|Q|}, {|A|}

Figure 1: Some of the important derivation rules of C OMPLX.

finished dealing with an initial sequential part and reaches elaborate on this in section 6. In order to be able to reason
a parallel composition. Note that the pre-condition {|P |} in about dynamic commands in OG, we must be able to anno-
the sequential OG statement disappears in the parallel one, tate them. Since program annotations are static, they must
as long as it implies the pre-condition of the assertion tree. not depend on the state of the program. Thus our framework
Also note that the OG rules ensure that the annotation tree restricts their use to dynamic commands that can be anno-
and the program match, e.g. a Parallel statement can only be tated statically. DynCom is derivable so long as an annota-
proved correct if provided with an AnnPar annotation. tion tree a can be provided and that it allows derivation of the
As explained in section 3, C OMPLX allows for nested command produced by the dynamic command d for any state
Parallel statements. Several conditions must be met when allowed by the assertion on DynCom. An additional require-
using the Parallel rule to derive a Parallel statement. Ev- ment for the annotation tree to be valid is that its first asser-
ery component of Parallel must itself be derivable. pres, tion must be allowed by the assertion on DynCom (i.e. r).
postcond and abrcond respectively return the annotation pre a returns the first assertion of the annotation tree a.
tree, the post-condition and the abrupt-condition of an el- The Guard rule is straightforward. For the command
ement of the list in AnnPar described earlier. While the Guard f g c, if the guard condition g is not satisfied, the fault
intersection of the post-conditions of all components must f must be allowed by the fault set F of the OG statement.
imply the post-condition of the overall Parallel , for abrupt- The rule asserts this by requiring that, when the fault is not
conditions only one of the components must satisfy the allowed by the OG statement, the assertion r allows more
abrupt-condition of the Parallel . This is explained by the states than the ones that do not satisfy the guard.
fact that exceptions can interrupt other components. The key The last interesting rule is Call . The annotation environ-
requirement for derivability of Parallel is interfree which ment Θ stores a list of annotation trees per function. Since
specifies interference freedom — we return to this definition a function can be called from multiple places and each call
in next section. may require a different set of assertions, multiple annotation
A derivation of Await b c requires a sequential derivation trees of the same function may be kept in the environment.
of c with the assertion r combined with the condition b as AnnCall provides a routine index to select which tree to use.
pre-condition. In addition, the command c must be deprived When deriving Call , the annotation environment needs to be
of Parallel and Call statements since they cannot be atomic correctly initialised such that the requested annotation tree
and thus are forbidden in Await. This restriction is achieved matches the function body. This way a derivable program
by the atom-com predicate and guarantees that a program cannot end in a Stuck state because of an undefined function
does not end in a Stuck state because of a non-atomic oper- call. Ideally, this index would be computed by the translation
ation found in an Await. from C to C OMPLX.
Dynamic commands are functions that produce a com-
mand from a state. They provide a general mechanism to 4.3 Interference Freedom
model programs that need to introspect their state. For in-
As explained in section 2, interference freedom states that,
stance, they could be used to model self-modifying code.
for every atomic command c extracted from parallel com-
However, for C verification their use is limited to restoring
ponents, all the commands it may be interleaved with have
the value of local variables when a function is called. We
their assertions preserved by the execution of c. C OMPLX’s

143
interfree definition follows the same principle as Hoare- obligations get easily discharged using Isabelle/HOL au-
Parallel’s, but with several important differences. tomation. This makes our framework ideal for concurrency
First, in order to support function calls we extract asser- verification as finding the right correctness assertions should
tions and atomics using relations instead of functions. Ex- be the bulk of the work for verifying a concurrent program.
tracting assertions and atomic commands from a program
requires going through every statement including statements 5. Soundness Proof
inside function bodies. To avoid divergence, the extraction Verification using logic rules and a VCG is much more effi-
functions must keep track of which functions have been pro- cient than reasoning directly with the semantics, but it needs
cessed. The resulting function must maintain a state and to be proven sound if we want to preserve the same level
becomes hard to use when induction is required. In addi- of trust. In this section we outline our proof that C OMPLX’s
tion, C OMPLX’s separation between program and annota- OG rules presented in section 4 are sound with respect to
tions makes it harder to extract assertions and atomics using the semantics presented in section 3. Namely we prove, in
a function. Since the annotation tree and the program struc- Isabelle/HOL, the following theorem (identical to S IMPL’s
ture are not synchronised by construction, a function would soundness theorem):
have to be partial or undefined if the annotation tree does not Γ, Θ `/F a c {|Q|}, {|A|} =⇒ Γ |=/F (pre a) c {|Q|}, {|A|}
match the structure of the program. To address these issues,
This states that any Hoare triple3 that is derivable from the
in C OMPLX we use relations instead of functions to extract OG-rules is valid, where validity is defined in terms of the
assertions and atomics. Using a relation, any mismatch be- small-step semantics (below e.g. Normal ‘ {|P |} denotes the
tween annotation tree and program structure simply results image of {|P |} under Normal , embedding this {|P |} into
in the relation not holding, and the infinite-recursion prob- extended states):
lem goes away since the relation does not have to terminate. Γ |=/F {|P|} c {|Q|}, {|A|} ≡
More importantly, by using a relation we can describe an in-
∀ s t c 0.
finite set of assertions/atomics, which is specifically required Γ ` (c, s) →∗ (c 0, t) −→
for DynCom. final (c 0, t) −→
Second, C OMPLX’s semantics is significantly more com- s ∈ Normal ‘ {|P|} −→
plicated than Hoare-Parallel’s. In particular, as the C OMPLX t∈/ Fault ‘ F −→
semantics executes the program, it reduces the program to a c 0 = Skip ∧ t ∈ Normal ‘ {|Q|} ∨ c 0 = Throw ∧ t ∈ Normal ‘ {|A|}
final command (i.e. Skip or Throw ) which denotes termina-
That is, a program c is called valid if final states of any of
tion of execution. This is visible on most of the small-step se-
its full executions without any faults from a state s satisfying
mantics rules presented in section 3, such as Parallel-Throw,
P , satisfy the relevant post-condition. More precisely, if c
Parallel-Skip... Consequently, Skip and Throw commands
executes, after multiple steps, into either Skip or Throw
have two purposes: they denote final configurations, and they
(denoted by final ) then the final state t satisfies Q if c0 is
also are legitimate commands that can be found in any given
Skip and A if c0 is Throw . Note that a sequence of small-
program. In the latter case, they must be annotated manu-
steps cannot reach both, Skip and Throw . It is also worth
ally. However, in the former case the C OMPLX framework
noting, that as a consequence of separating annotations from
must automatically generate assertions for them. Typically,
programs, the notion of validity is purely semantical, thus
the assertion on a Skip will be the assertion of the next com-
completely independent from annotations which are only
mand, or, if it is the last command of the program, the post-
needed for derivability.
condition. Hence, the relation that extracts assertions takes
We now outline the main challenges in the soundness
the post-condition and the abrupt-condition of the program
proof that proceeds by induction on the structure of the OG-
and generates the appropriate assertions for every semantics
rules. For the sake of brevity, in the following we will focus
rule that leads to a final configuration.
on the cases when all considered states are Normal : apart
Finally, since C OMPLX allows nested Parallel state-
from these we get several corner cases, such as that guards
ments, assertions need to be collected recursively on each
can fail only within specified Fault states or absence of un-
of the parallel components.
defined function calls. These are, however, of technical na-
4.4 VCG ture and do not contribute much to the structure and com-
In order to automate the creation of verification conditions plexity of the overall proof.
for programs in C OMPLX, we ported and extended Hoare- All OG-rules, beside those for parallel composition and
Parallel’s VCG. We added support for several constructors, synchronisation, retain their S IMPL form, such that in these
including Catch, Call , Guard and DynCom. This involved cases we proceed similarly to the sequential setting. This
writing Isabelle/HOL tactic rules to decompose the deriva- changes, of course, as soon as we reach the parallel com-
tion of these commands and convert interference freedom position and await cases.
goals to OG statements showing that assertions are pre- 3 Technically, this is more a Hoare quadruple but we still use the more
served. As in Hoare-Parallel, most of the generated proof traditional term of triple.

144
The major challenges arise in the proof of the paral- 6. Case Study
lel case, i.e. when c = Parallel cs. We can assume all the We used our C OMPLX framework to reproduce the proof
premises of the OG-rule PARALLEL (see Figure 1), in partic- of correctness of a few examples of concurrent algorithms
ular that interference freedom holds for the parallel compo- that had been verified within Hoare-Parallel, including the
nents cs with respect to the annotation. Moreover, we can as- proof of Peterson’s solution to the mutual exclusion prob-
sume Γ ` hParallel cs, Normal xi →∗ hc0 , Normal yi with lem [Prensa Nieto 2002]. Our proofs can be found on-
x satisfying the annotated pre-condition and c0 being Skip line [COMPLX] and were very easily achieved once our
or Throw . What we need to prove is that y satisfies the rele- framework was complete. This shows that C OMPLX is ro-
vant post-condition. For this we induct on the closure of the bust and backward compatible with Hoare-Parallel, as none
small-step relation, and the challenge is to show that all the of the proofs required extra work. The VCG generates ap-
assumed conditions (from the premise of the OG rule, e.g. proximately the same number of proof obligations and dis-
interference freedom) are preserved by each execution step charging them takes a similar processing time. These exam-
(to be able to apply the induction hypothesis). This is a chal- ples, however, did not exercise any C-specific features.
lenge because each step ‘consumes’ a part of the program, To demonstrate the practicality of our framework in veri-
which needs to be reflected in the annotation tree. We cap- fying concurrent C code, we created an example (also avail-
ture this by a separate lemma, where we collect all the neces- able online [COMPLX]) of a concurrent C program that ex-
sary properties relating pre- and post-configurations of any ercises the specific features that C OMPLX supports. In par-
small-step. That is, if Γ ` hc, Normal xi → hc0 , Normal yi ticular, our example uses exceptions, guards and function
holds for any c, x, c0 , y, and the program c is derivable with calls, all of which are not supported by Hoare-Parallel.
an annotation structure a by the OG-rules such that s satis- In our example, we extracted manually the program
fies the annotated pre-condition, then we can find an anno- model from the C source code. The C program and the C OM -
tation structure a0 such that c0 is derivable with a0 , y satis- PLX program are both less than 20 lines, and the whole
fies the pre-condition in a0 and, moreover, any assertion or model is ≈230 lines of Isabelle/HOL definitions, includ-
atomic of a0 is an assertion or atomic of a, respectively. Since ing the complete set of assertions used to annotate the pro-
the program c is an arbitrary C OMPLX program, we induct gram and verify its correctness. The VCG generates 688
on the structure of c. Here again, only the await and paral- conditions and most of them are easily discharged using Is-
lel cases are more involved. For await we can rely on the abelle/HOL automation. Once again, the bulk of the work
canonical restriction that the body of any Await-construct lies in finding the right correctness assertions.
is purely sequential, i.e. a S IMPL program in fact. In the The aim of the program is to compute the combined sum
parallel case, however, we have to deploy our interference of all the elements of multiple arrays. It does this by running
freedom assumption to show that any post-state of the whole a number of threads in parallel, each computing the sum of
parallel construct will satisfy annotated conditions regard- elements of one of the arrays, and then adding the result to
less of which of the constituting components does its small a global variable gsum shared by all threads. We restrict
step. To this end we need to establish a connection between the example to two arrays and threads, but this could be
the small-step semantics and atomics as follows. Any pro- generalised: we would then just need to generate accordingly
gram transition Γ ` hc, Normal xi → hc0 , Normal yi, where more copies of the function sumarr , pairwise disjoint in
Normal x satisfies the annotated pre-condition and x 6= y, local variables, such that each thread can invoke its own copy
can only happen due to an atomic subcomponent cc of c that of sumarr . The correctness statement for this program is:
performs the step Γ ` hcc, Normal xi → hSkip, Normal yi.
Now, the interference freedom property states that each of Γ, Θ |`
`/F {|precond|}
such atomic steps preserves assertions of any component COBEGIN
other than the one that performs the step. This gives us the SCHEME [0 ≤ m < 2]
call-sumarr m
preservation we need to carry assumptions over single steps
{|local-postcond m|}, {|False|}
of execution. COEND
For the proof of the top-level Await case we similarly {|postcond|}, {|False|}
can assume Γ ` hAwait b cc, Normal xi →∗ hc0 , Normal yi
with x satisfying the annotated pre-condition, c0 being Skip The SCHEME syntax models a parametric number of par-
or Throw , and Await b cc being derivable by the OG-rules. allel programs. Here we use it to model the creation of
Moreover, by induction hypothesis we also know that the two threads running concurrently, each calling the func-
await-body cc is valid. On the other hand, from the se- tion sumarr . The post-condition (definition not shown)
mantics of Await we can conclude that y can only be ob- states that the global variable gsum is indeed equal to
tained by a certain number of small-step transformations the combined sum of all elements of all arrays. Since the
of hcc, Normal xi until a Skip or Throw configuration is function sumarr cannot terminate with an exception, the
reached, establishing the desired result. abrupt-condition is false, which forces us to prove that all
exceptions are caught. As explained in section 4, prov-

145
ing interference freedom also requires that we specify the
post-condition (local-postcond) and abrupt-condition (false call-sumarr m ≡
{|local-precond m|} CALLX (sumarr-init m)
again) of the parallel component.
{|sumarr-precond m|} ( 00sumarr 00, m) m
To begin we explain the state of the program, then how we (sumarr-restore m) (λ- -. Skip)
model function calls, and finally how the sumarr function is {|sumarr-restore-post m|} {|sumarr-return-post m|}
defined. {|False|} {|False|}
State The state of the program is modelled with the
following record: Figure 2: Annotations for the function sumarr .

record sumarr state =


callx init body restore return =
(* function arguments *)
DynCom (λs. TRY
tarr :: “routine ⇒ word32 array”
init;; body
tid :: “routine ⇒ word32”
CATCH
(* local variables of threads *)
restore s;; THROW
ti :: “routine ⇒ word32”
END;;
tsum :: “routine ⇒ word32”
DynCom (λt. restore s;; return s t))
(* global variables *)
garr :: “(word32 array) array” Figure 3: Argument passing and scoping for function calls.
gsum :: word32
gdone :: word32
glock :: nat SCHEME ) used to specify which copy of a local variable is
accessed. CALLX is syntactic sugar for calling a function
We now explain the need for the routine argument. Major while passing arguments and implementing scoping, i.e. sav-
challenges arise when attempting to verify parallel programs ing and restoring local variables. The computation is done
that make use of function calls. In a sequential context, by a function callx shown in Figure 3 (CALLX combines it
a call to a function named f in a state s means that we with annotations, as we will explain shortly). The process of
just lookup the body of f in the procedure environment Γ, calling a function involves several steps:
continue with the execution of Γf in the state s and return 1. Saving the value of local variables by keeping a copy of
to the calling routine afterwards. In a concurrent setting, the state.
however, this execution could be interleaved with another
2. Initialising local variables and function arguments by
call of f invoked by a different thread. Thus, if Γf uses some
updating the state.
local variables, the model of the overall parallel program
might not behave as in reality, as both invocations of f can 3. Executing the function body.
interfere on the same local variables. Therefore, in our state, 4. Restoring the value of local variables using the copy of
function arguments and local variables are modelled as a the state.
mapping from routine index to value. This allows concurrent
5. Extracting the return value of the function from the state.
executions of a function to use different instances of the
variables. Saving and restoring local variables is required to support
In contrast, global variables are shared by all threads, so recursive functions and is equivalent to setting up and tearing
they are not protected by a routine index. We use garr to down the stack frame in C. Steps 1 and 5 of function calls
store two arrays of machine 32-bit words that will be passed are both implemented using DynCom as seen in Figure 3.
to each thread via argument tarr . The global variable gsum The first DynCom is used to keep a copy of the state
is used to store the total result, whereas tsum stores the that is later used for restoring local variables. As we can see,
local result used by each thread. The bit-field gdone is used callx is complicated by exceptions that may cross function
to indicate whether a thread has finished its computation. boundaries. When an exception is uncaught, the local vari-
Finally, the threads use glock as a mutually exclusive lock in ables must first be restored before the exception is propa-
order to protect the shared variables gsum and gdone. gated.
For the remainder of this section, {|...|} denotes the places Steps 2 and 4 use the provided functions init and restore.
where assertions are required. To improve the readability In our example, there are two arguments being passed to
and to highlight the similarity between input source and sumarr , the array tarr and the thread identifier tid . The
C OMPLX model, we display our models without most of the initialisation and restore functions are:
assertions.

Function calls We define call-sumarr as shown in Fig-


ure 2. The parameter m is the routine index (from the

146
parametrise sumarr with m, which is used as a routine in-
sumarr-init m ≡ dex to select which local variables to use.
λs. s(|tarr := (tarr s)(m := (garr s)[m] ), The first two lines initialise the local variables tsum and
tid := (tid s)(m := m + 1), ti := (ti s)(m := undefined), ti to zero. Note that a for-loop is trivially converted to a
tsum := (tsum s)(m := undefined)|) while-loop by moving the loop counter update to the end
of the loop body. The while-loop is wrapped inside a try-
catch statement because C OMPLX, just like S IMPL, uses
sumarr-restore m ≡ exceptions to model early exit of a loop. Hence, the break
λs t. t(|tarr := (tarr t)(m := tarr s m), tid := (tid t)(m := tid s m), statement present in the C code is replaced with a Throw in
ti := (ti t)(m := ti s m), tsum := (tsum t)(m := tsum s m)|) C OMPLX, and the CATCH block only contains a Skip, i.e.
To reason about function calls in OG, the user also has do nothing. This means that when the exception is thrown, it
to provide assertions for every step in the control flow of has the effect of breaking out of the loop.
callx . To facilitate this, there is an equivalent helper function One of the most important aspects of verifying C code
ann callx . The arguments of the CALLX statement are is proving the absence of behaviours undefined by the C
then the commands for dealing with argument passing and standard. In both S IMPL and C OMPLX undefined behaviours
scoping, the corresponding annotations, and the function are usually specified using guards. For instance, in our case
name and index used when looking up the procedure and there could be undefined behaviour if an invalid pointer is
annotation environment. dereferenced. Therefore, every time a pointer is accessed it
One restriction of our current implementation of CALLX must be protected by a guard forcing us to prove that the
is that both initialising and restoring local variables is per- pointer is indeed valid.
formed in one step. This does not match the reality of C, In Figure 4b, since tarr is a pointer, every access is
which uses multiple instructions and can potentially be in- guarded with an array-in-bound check which guarantees
terleaved. We believe that it would be relatively straightfor- that tarr is a valid pointer and that the index ti is less
ward to change our framework to use multiple steps, one for than the length of the array. If the guard is not satisfied the
each local variable. Additionally, we should be able to create program returns the fault InvalidMem indicating an invalid
syntactic sugar that hides many of these details from the end memory access. Having explicit guard commands in C OM -
user. PLX also allows us to reason about concurrent programs that
In Figure 2, local-precond corresponds to the first asser- can actually end in a specific Fault state. For instance, we
tion of the parallel component, whereas summar-precond is are able to prove that in some circumstances a program is
the assertion once the arguments and local variables have guaranteed to result in a Fault state.
been initialised. The other assertions describe the state be- After the loop, the model calls the function lock , to ac-
fore restoring local variables (summar-restore) and before quire the global mutex that protects the shared variables
extracting the return value from the state (summar-return), gsum and gdone. Since lock and unlock only access a
for normal termination of the function. For abrupt termina- global variable (the mutex) and do not take any arguments,
tion these assertions are false, as the function sumarr cannot their call does not require saving and restoring of local vari-
terminate with an exception. ables. The definition of lock and unlock follows:
lock m ≡ {|...|} AWAIT glock = 0 THEN glock := 1 END
The sumarr function Returning to our case study, Fig-
ure 4a shows the body of the sumarr function, as it would unlock m ≡ {|...|} glock := 0
be implemented in C. The pointer tarr refers to the array of
The mutex is modelled by using glock , a global variable
unsigned integers that is being summed and tid is a thread
set to 1 when the lock is held and 0 otherwise. The semantics
identifier of value 1 or 2 depending on the thread.
of Await guarantees that only one thread can be inside the
The loop calculates the sum of each element of the ar-
lock at a time.
ray and at each iteration if the sum or the current element
exceeds MAXSUM, we break out of the loop and cap tsum Summary C OMPLX was designed to reason about an ac-
at MAXSUM. This prevents potential word overflows, as- curate representation of the C code, without requiring that
suming that NSUM and MAXSUM are chosen appropriately. programmers radically change their programming style and
After the loop, we invoke the lock and unlock functions to habits. A key feature of C OMPLX is the syntactic sugar
protect the global variables gsum and gdone. In the C code, which allows annotating programs directly on the program
lock and unlock are implemented using a mutex. model, despite having a separate datatype for annotation tree
Figure 4b shows the C OMPLX model in Isabelle/HOL of and program. This gives us the best of both worlds: a user-
this function. As seen in subsection 4.1, C OMPLX’s syntac- friendly framework for annotating program and a neat lan-
tic sugar allows the program to be directly annotated. Addi- guage abstract syntax which is not cluttered with irrelevant
tionally, concurrent executions of functions require different annotations. The lack of exceptions in Hoare-Parallel would
instances of any local variables. Therefore, in Figure 4b we force reimplementing our code to avoid having a break

147
sumarr m ≡
{|...|} tsum := tsum(m := 0);;
void sumarr ( unsigned i n t ∗ t a r r , {|...|} ti := ti(m := 0);;
unsigned i n t t i d )
{
TRY {|...|} WHILE ti m < NSUM INV {|...|}
unsigned i n t t i ; DO {|...|} (InvalidMem, {|array-in-bound (tarr m) (ti m)|}) 7−→
u n s i g n e d i n t tsum ; {|...|} tsum := tsum(m := tsum m + array-nth (tarr m) (ti m));;
{|...|} (InvalidMem, {|array-in-bound (tarr m) (ti m)|}) 7−→
tsum = 0 ; {|...|} IF MAXSUM ≤ tsum m ∨ MAXSUM ≤ array-nth (tarr m) (ti m)
f o r ( t i = 0 ; t i < NSUM; t i ++) { THEN {|...|} tsum := tsum(m := MAXSUM);;
tsum += t a r r [ t i ] ;
{|...|} THROW
i f ( tsum >= MAXSUM | |
t a r r [ t i ] >= MAXSUM) { ELSE {|...|} SKIP
tsum = MAXSUM; FI;;
break ; {|...|} ti := ti(m := ti m + 1)
} OD
} CATCH {|...|} SKIP END;;
lock ( ) ;
{|...|} SCALL ( 00lock 00, 0) m;;
gsum += tsum ;
gdone | = t i d ; {|...|} gsum := gsum + tsum m;;
unlock ( ) ; {|...|} gdone := (gdone OR tid m);;
} {|...|} SCALL ( 00unlock 00, 0) m

(a) C code
(b) C OMPLX model

Figure 4: The C code and matching C OMPLX model of our case study

statement in the middle of the loop. Furthermore, support the simple high-level language IMP) and we introduced a
for guard statements is critical to enable C verification, be- technique that we called await-painting, essentially painting
cause they ensure that the semantics of the code is defined. our program with Await statements to limit the concurrency
Finally, supporting function calls is a key requirement for to places where it actually occurs. This technique allowed us
our framework, which we intend to use on larger scale veri- to proof-engineer an Isabelle/HOL tactic that automatically
fication projects. discharges most of the verification conditions generated by
OG. We successfully used the tactic to verify an abstract
7. Related Work model of the eChronos OS scheduling behaviour. C OMPLX
will enable us to extend this verification to the C implemen-
Logics for Concurrency Over the years, many logics were
tation.
developed for reasoning about concurrency, the oldest and
In C OMPLX, just like S IMPL, the notion of state is ab-
most straightforward of which is OG. OG provides, however,
stract: we propose a generic language for reasoning about
no modular way to reason about memory and quickly leads
concurrent imperative code. Modelling memory is orthogo-
to an explosion in the number of verification conditions
nal to the work done in this paper. The C-to-Isabelle parser
that need to be proven. Rely-Guarantee (RG) [Jones 1983],
defines a concrete notion of state on top of S IMPL which, for
Concurrent Separation Logic (CSL) [OHearn 2007], and a
instance, can be reasoned about using separation logic [Tuch
number of more recent combinations and extensions of these
et al. 2007]. Building a framework in the spirit of FCSL
(e.g. [Vafeiadis 2008; da Rocha Pinto et al. 2014]) have been
(fine-grained concurrent separation logic) [Nanevski et al.
developed since to overcome the modularity issues of OG.
2014; Sergey et al. 2015] on top of the C OMPLX language
The separation-based logics typically rely on ownership over
would be interesting future work.
shared state: threads need to lock their accesses to shared
Recent work showed that, as is, OG is unsound for weak
state and ownership can be transferred along acquire/release
memory models but can be extended in a sound logic by
atomic memory accesses.
strengthening its interference-freedom condition [Lahav and
In recent work [Andronick et al. 2015, 2016], we found
Vafeiadis 2015]. We could look at a similar approach if we
that using the simple OG method is suitable for our reason-
want to support weak memory models in the future.
ing of interrupt-induced concurrency in racy OS code. In that
code, the OS API functions, the scheduler and the interrupt Tools for Verification of Concurrent C We focus here
handlers all concurrently modify shared variables without on tools that allow the verification of specific properties
any synchronisation (in order to meet stringent low-latency or specifications, rather than e.g. static analysers that can
requirements). The correctness argument needs to rely on only detect specific classes of errors. VCC [Cohen et al.
fine-grain assertions at these sharing points; it cannot rely 2009] is an industrial-strength verification environment for
on some atomicity or ownership argument. We used OG (at low-level concurrent system code. It is an assertional, auto-

148
matic, deductive code verifier for C, where specifications in to verify concurrent operating systems, such as the inter-
the form of function contracts, data invariants, and loop in- ruptible eChronos embedded operating system or multicore
variants are added to the C code to guide VCC. From the seL4.
annotated program, VCC generates verification conditions,
which it then tries to discharge using the automatic theo- Acknowledgments
rem prover Z3 [de Moura and Bjørner 2008] or through the
This research is supported by the Air Force Office of Scientific
Boogie verifier [Barnett et al. 2006]. VCC has been used,
Research, Asian Office of Aerospace Research and Development
among others, to verify the Microsoft Hyper-V hypervisor
(AOARD) and U.S. Army International Technology Center - Pa-
and has also been used in the Verisoft XT project [Verisoft
cific under grant FA2386-15-1-4055.
XT]. Moreover, Isabelle/HOL was used as a backend to
VCC for the verification of certifying graph algorithms from
LEDA [Alkassar et al. 2014]. When the C-to-Isabelle parser References
was open-sourced, the LEDA verification project switched E. Alkassar, S. Böhme, K. Mehlhorn, and C. Rizkallah. A frame-
to only using S IMPL and the C-to-Isabelle parser and redid work for the verification of certifying computations. Journal of
their verification completely within Isabelle/HOL, in order Automated Reasoning, 52(3):241–273, 2014. ISSN 0168-7433.
to provide higher trust guarantees [Noschinski et al. 2014; doi: 10.1007/s10817-013-9289-2.
Rizkallah 2015]. Unlike an LCF based theorem prover (e.g. S. Amani, A. Hixon, Z. Chen, C. Rizkallah, P. Chubb, L. O’Connor,
Isabelle/HOL or Coq [Bertot and Castéran 2004]), VCC re- J. Beeren, Y. Nagashima, J. Lim, T. Sewell, J. Tuong, G. Keller,
lies on a large trusted computing base that includes the en- T. Murray, G. Klein, and G. Heiser. Cogent: Verifying high-
assurance file system implementations. In ASPLOS, Atlanta,
tire VCC engine and Z3 [Noschinski et al. 2014; Rizkallah
GA, USA, Apr 2016. doi: 10.1145/2872362.2872404.
2015]. Similar to VCC, VeriFast [Jacobs et al. 2010] relies
on Z3. We would like to enable reasoning about concurrency, J. Andronick, C. Lewis, and C. Morgan. Controlled Owicki-Gries
within an LCF based theorem prover in order not to compro- concurrency: Reasoning about the preemptible eChronos em-
bedded operating system. In Workshop on Models for Formal
mise on trust.
Analysis of Real Systems (MARS), 2015.
A number of recent efforts provide tools to reason about
concurrency in Coq. Most of these efforts are based on J. Andronick, C. Lewis, D. Matichuk, C. Morgan, and C. Rizkallah.
Proof of OS scheduling behavior in the presence of interrupt-
CSL and primarily focus on modular reasoning about non-
induced concurrency. In International Conference on Interactive
racy shared memory. The Verified Software Toolchain [Ap- Theorem Proving, Nancy, France, aug 2016.
pel 2012] provides machine-checked guarantees that the
A. W. Appel. Verified software toolchain. In A. Goodloe and
CSL assertions about concurrent C code with primitive lock
S. Person, editors, NASA Formal Methods - 4th International
operations hold down to the machine-language program.
Symposium, NFM 2012, Norfolk, VA, USA, April 3-5, 2012. Pro-
Iris [Jung et al. 2015, 2016] is a general and expressive logic ceedings, volume 7226 of Lecture Notes in Computer Science,
with a simple set of verified primitive mechanisms and proof page 2. Springer, 2012.
rules for modular reasoning about shared memory. Once
M. Barnett, B.-Y. E. Chang, R. DeLine, B. Jacobs, and K. R. M.
again, we are targeting potentially-racy high-performance Leino. Boogie: a modular reusable verifier for object-oriented
code for which OG fine-grain assertions are well-suited. programs. In 4th FMCO, volume 4111 of LNCS, pages 364–
387, Amsterdam, The Netherlands, 2006. Springer.
8. Conclusion and Future Work Y. Bertot and P. Castéran. Interactive Theorem Proving and Pro-
gram Development. Coq’Art: The Calculus of Inductive Con-
In this paper we have presented our C OMPLX framework
structions. Texts in Theoretical Computer Science. An EATCS
for sound verification of concurrent imperative code in Is- Series. Springer, 2004. ISBN 3-540-20854-2. doi: 10.1007/
abelle/HOL. We have emphasised how we use the Owicki- 978-3-662-07964-5.
Gries method in order to extend the S IMPL tool to cope
E. Cohen, M. Dahlweid, M. Hillebrand, D. Leinenbach, M. Moskal,
with concurrency. This way our framework inherits support T. Santen, W. Schulte, and S. Tobies. VCC: A practical system
for function calls and exception handling from S IMPL. The for verifying concurrent C. In S. Berghofer, T. Nipkow, C. Ur-
presented case-study illustrates how these features can be ban, and M. Wenzel, editors, 22nd TPHOLs, volume 5674 of
utilised in practical verification. LNCS, pages 23–42, Munich, Germany, 2009. Springer. doi:
Future work includes more proof engineering to increase 10.1007/978-3-642-03359-9 2.
ease of use, integration with the C-to-Isabelle parser, and COMPLX. cf. http://ts.data61.csiro.au/projects/
definition of more concrete notions of states. concurrency/complx.
With the work presented here, we bridge the gap between P. da Rocha Pinto, T. Dinsdale-Young, and P. Gardner. TaDA: A
the verification of abstract algorithms and that of their imper- logic for time and data abstraction. volume 8586 of LNCS, pages
ative implementations. We plan to extend the C-to-Isabelle 207–231. Springer, 2014. doi: 10.1007/978-3-662-44202-9 9.
parser to translate C code into C OMPLX code to provide URL http://dx.doi.org/10.1007/978-3-662-44202-9_
guarantees for concurrent low-level C code, with the aim 9.

149
L. M. de Moura and N. Bjørner. Z3: An efficient SMT solver. T. Nipkow, L. Paulson, and M. Wenzel. Isabelle/HOL — A
In TACAS, volume 4963 of LNCS, pages 337–340, Budapest, Proof Assistant for Higher-Order Logic, volume 2283 of LNCS.
Hungary, Mar 2008. Springer. ISBN 978-3-540-78799-0. doi: Springer, 2002. ISBN 978-3-540-43376-7. doi: 10.1007/
10.1007/978-3-540-78800-3 24. 3-540-45949-9.
B. Jacobs, J. Smans, and F. Piessens. A quick tour of the verifast L. Noschinski, C. Rizkallah, and K. Mehlhorn. Verification of cer-
program verifier. In Programming Languages and Systems - 8th tifying computations through AutoCorres and Simpl. In J. M.
Asian Symposium, APLAS 2010, Shanghai, China, November 28 Badger and K. Y. Rozier, editors, NASA Formal Methods, vol-
- December 1, 2010. Proceedings, pages 304–311, 2010. ume 8430 of LNCS, pages 46–61. Springer, 2014.
C. B. Jones. Tentative steps towards a development method for P. W. OHearn. Resources, concurrency, and local reasoning. Theor.
interfering programs. Trans. Progr. Lang. & Syst., 5(4):596–619, Comput. Sci., 375(1-3):271–307, Apr 2007. ISSN 0304-3975.
1983. doi: 10.1016/j.tcs.2006.12.035.
R. Jung, D. Swasey, F. Sieczkowski, K. Svendsen, A. Turon, S. Owicki and D. Gries. An axiomatic proof technique for parallel
L. Birkedal, and D. Dreyer. Iris: Monoids and invariants as an programs. Acta Informatica, 6:319–340, 1976.
orthogonal basis for concurrent reasoning. In S. K. Rajamani
L. Prensa Nieto. Verification of parallel programs with the Owicki-
and D. Walker, editors, Proceedings of the 42nd Annual ACM
Gries and rely-guarantee methods in Isabelle/HOL. PhD thesis,
SIGPLAN-SIGACT Symposium on Principles of Programming
Languages, POPL 2015, Mumbai, India, January 15-17, 2015, T.U. München, 2002.
pages 637–650. ACM, 2015. C. Rizkallah. A Simpl shortest path checker verification. In
R. Jung, R. Krebbers, L. Birkedal, and D. Dreyer. Higher-order Proceedings of Isabelle Workshop 2014, 2014.
ghost state. In J. Garrigue, G. Keller, and E. Sumii, editors, Pro-
C. Rizkallah. Verification of program computations. PhD the-
ceedings of the 21st ACM SIGPLAN International Conference
sis, Saarland University, 2015. URL http://scidok.sulb.
on Functional Programming, ICFP 2016, Nara, Japan, Septem-
uni-saarland.de/volltexte/2015/6254/.
ber 18-22, 2016, pages 256–269. ACM, 2016.
N. Schirmer. Verification of Sequential Imperative Programs in
G. Klein, J. Andronick, K. Elphinstone, G. Heiser, D. Cock, P. Der-
Isabelle/HOL. PhD thesis, Technische Universität München,
rin, D. Elkaduwe, K. Engelhardt, R. Kolanski, M. Norrish,
2006.
T. Sewell, H. Tuch, and S. Winwood. seL4: Formal verifica-
tion of an operating-system kernel. CACM, 53(6):107–115, Jun N. Schirmer. A sequential imperative programming language
2010. doi: 10.1145/1743546.1743574. syntax, semantics, hoare logics and verification environment.
Archive of Formal Proofs, Feb 2008. ISSN 2150-914x. http://
O. Lahav and V. Vafeiadis. Owicki-Gries Reasoning for Weak
isa-afp.org/entries/Simpl.shtml, Formal proof devel-
Memory Models, pages 311–323. Springer Berlin Heidelberg,
opment.
Berlin, Heidelberg, 2015.
I. Sergey, A. Nanevski, and A. Banerjee. Mechanized verification
K. Mehlhorn and S. Näher. The LEDA Platform for Combinatorial
of fine-grained concurrent programs. In ACM SIGPLAN Notices,
and Geometric Computing. Cambridge University Press, 1999.
volume 50, pages 77–87. ACM, 2015.
T. Murray, D. Matichuk, M. Brassil, P. Gammie, and G. Klein. Non-
interference for operating system kernels. In Chris Hawblitzel H. Tuch, G. Klein, and M. Norrish. Types, bytes, and separation
and Dale Miller, editor, CPP, pages 126–142, Kyoto, Dec 2012. logic. In Martin Hofmann and Matthias Felleisen, editor, POPL,
Springer. ISBN 978-3-642-35307-9. pages 97–108, Nice, France, Jan 2007. ACM. ISBN 1-59593-
575-4.
A. Nanevski, R. Ley-Wild, I. Sergey, and G. A. Delbianco. Com-
municating state transition systems for fine-grained concurrent V. Vafeiadis. Modular fine-grained concurrency verification. PhD
resources. In European Symposium on Programming Languages thesis, University of Cambridge, Computer Laboratory, jul 2008.
and Systems, pages 290–310. Springer, 2014. Verisoft XT. Verisoft XT. http://www.verisoftxt.de, 2010.

150

You might also like