Undergraduate Research Opportunity Program
(UROP) Project Report
Mechanical soundness proof of separation logic in sequential
language and concurrent language
By
Le Xuan Bach
Department of Computer Science
School of Computing
National University of Singapore
2009/2010
Undergraduate Research Opportunity Program
(UROP) Project Report
Mechanical soundness proof of separation logic in sequential
language and concurrent language
By
Le Xuan Bach
Department of Computer Science
School of Computing
National University of Singapore
2009/2010
Project No: U131010
Advisor: Dr. Aquinas Hobor
Deliverables:
Report: 1 Volume
Abstract
In this UROP project, we use the concept of separation logic[6] to apply in program verification.
The the first semester, we built the operational semantic of sequential language. Next ,we
developed the Hoare rule[7] for each command in our language and proved its correctness. In the
following semester, we investigated the concurrent model, developed the operational semantics
for the concurrent language.At the same time, we developed a new model for separation algebra
called the Diamond join as well as a model of swapped memories.
Subject Descriptors:
D.3.1 [PROGRAMMING LANGUAGES]: Formal Definitions and Theory Semantics
F.3.1 [LOGICS AND MEANINGS OF PROGRAMS]: Specifying and Verifying and Reasoning about Programs Logics of program
F.4.1 [MATHEMATICAL LOGIC AND FORMAL LANGUAGES]:Mathematical Logic Mechanical theorem proving
Keywords:
Programming language, mathematical logic, program verification
Implementation Software and Hardware:
Coq-A theorem prover
Table of Contents
Title
i
Abstract
0.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . .
0.2 Separation logic . . . . . . . . . . . . . . . . . . . . . . . . . .
0.2.1 From Hoare logic to Separation logic . . . . . . . . . .
0.2.2 A simple model of separation logic . . . . . . . . . . .
0.3 The soundness of sequential separation logic . . . . . . . . . .
0.4 Diamond: A new separation algebra . . . . . . . . . . . . . .
0.5 Designing new concurrent separation logic . . . . . . . . . . .
0.5.1 The study of a concurrent language with share model
0.5.2 A new concurrent language . . . . . . . . . . . . . . .
0.5.3 Bijection between start c and c1 ||c2 . . . . . . . . . . .
0.5.4 Erased concurrent step relation . . . . . . . . . . . . .
0.5.5 A new look at sequential step relation . . . . . . . . .
0.6 Improved mechanized list separation library . . . . . . . . . .
0.7 In progress : Soundness of new Concurrent Separation Logic .
0.8 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . .
References
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
ii
iv
v
v
vi
vii
xiv
xv
xv
xvii
xvii
xviii
xxiii
xxvi
xxix
xxix
xxxi
iii
0.1
Introduction
Separation logic is an elegant solution to the pointer aliasing problem of Hoare logic.Calcagno,
O’Hearn and Yang[8] present a semantics of separation logic based on structures they call
separation algebra. Based on this result, Hobor et al.[4] developed a more flexible separation
algebra that has more pleasing mathematical properties and that is better suited to the task of
generating useful separation logics. They also built a Coq library for separation algebra. In his
thesis, Hobor[1] implemented a concurrent model that uses separation logic as the underlying
property and proved its correctness. Inspired by his work, in our project, we would like to
take advantages of results in his thesis to develop new useful results. The following are out
contributions:
• We developed the operational semantics for sequential language
• We developed the Hoare rules for sequential language
• We proved the correctness of the Hoare rules
• We developed the operational semantics for the concurrent language
• We developed the Hoare rules for concurrent language
• We proved the determinism of our operational semantics
• We built a new model for separation logic called the Diamond join
• We built a model of swapped memories
• We improved the join list library
The structure of this report is as follow:
• In chapter 2, we will introduce some background on Hoare logic and separation logic as
well as a simple model for separation logic.
• In chapter 3, we will introduce the sequential model as well as its correctness.
iv
• In chapter 4, we will introduce a new model for separation logic called Diamond join.
• In chapter 5, we will give some insights about the concurrent model, the erased cncurrent
model as well as the model of swapped memories.
• In chapter 6, we will present how we improved the join list library in Coq
• In chapter 7, we will present what we have yet achieved as well as the difficulties
• In chapter 8, we give our conclusion to the project.
The project source code in Coq can be accessed at:
http://www.comp.nus.edu.sg/∼lexbach/UROP
0.2
0.2.1
Separation logic
From Hoare logic to Separation logic
Program verification is an important but difficult task in computing since we all know there
were serious fatalities due to defected softwares. While testing helps reduce the number of bugs,
it does not guarantee that the programme is bug-free. Therefore, many formal methods were
developed recently to prove the correctness of the computer programmes, one of them is the
axiomatic semantics that utilizes the Hoare logic.
Hoare logic[7] is a set of rules applied on the Hoare tuples. A Hoare tuple {P } c {Q} contains
a precondition P , a command c and a post condition Q with a guarantee that if we have P , we
will have Q after the execution of c.An example of Hoare rule is shown as follow :
{e ⇓ n} x := e {x 7→ n}
Assign
which means if an expression e is evaluated to value n, after executing the command x := e,
the value of variable x will be n.While all the rules look reasonable, it becomes complicated
for the case of pointer alias.For example, the value of y in the postcondition Q of the Hoare
tuple {x 7→ 1 ∧ y 7→ 1} x := 2 {Q} can be either 1 or 2. That is when the separation logic
v
x ⊕ y = z 1 → x ⊕ y = z2 → z1 = z2
(1)
x⊕y =y⊕x
(2)
x ⊕ (y ⊕ z) = (x ⊕ y) ⊕ z
(3)
x1 ⊕ y = z → x2 ⊕ y = z → x1 = x2
(4)
∀x, ∃u, x ⊕ u = x
(5)
a⊕a=b→a=b
(6)
Figure 1: Separation algebra axioms
come into handy : by introducing a new operator ? , a condition P ? Q makes sure we can split
the computer memory into two disjoint parts in which P and Q are satisfied separately. If we
replace the conjunction operator of the above example by ?, then it is trivial to find the post
condition Q : {x 7→ 1 ? y 7→ 1} x := 2 {x 7→ 2 ? y 7→ 1}. The separation logic is built from
the separation algebra which is axiomatized by six axioms: In Coq, instead of defining ? as a
function of two parameters, we define it as a relation of three parameters :
Definition sepalg(A : Type) : Type := A → A → A → Prop.
a ? b = c is equivalent to join a b c in Coq.
0.2.2
A simple model of separation logic
Here we will show a simple example of a separation logic model. The domain of the model consists of element of type option A (given a type A, type option A = {Some a, a ∈ A} ∪ {N one})
that satisfy the following constraints:
vi
None ? None = None
None ? Some a = Some a
Some a ? None = Some a
In Coq, it is written as :
Definition ojoin(A : Type)(on1 on2 on3 : optionA) : Prop :=
match(on1, on2, on3)with
|(Some x, None, Some y) => x = y
|(None, Some x, Some y) => x = y
|(None, None, None) => True
| => False
end.
Lemma 1 The ojoin relation is a model of separation logic.
Proof.
It is shown in Coq that the above definition satisfies the six axioms of separation logic. Hence,
this model inherits every properties derived from separation algebra.
Qed.
0.3
The soundness of sequential separation logic
A complete description of our sequential language is as follow :
In Coq, it is written as :
vii
Numerical Expression : e := c| x| e1 + e2 | e1 ∗ e2
Boolean Expression : b := true| f alse|¬b|b1 → b2 | b1 ∧ b2 | b1 ∨ b2 |
e1 = e2 | e1 < e2
Command : c := skip| x := e| x := [e]| [e1 ] := e2 | free e| x := new e|
c1 ; c2 | if b c1 c2 | while b c
Figure 2: Language description
InductiveExp : Type :=
|eVar : forallv : Var, Exp
|eCon : forallc : nat, Exp
|eNeg : foralle : Exp, Exp
|eAdd : foralle1e2 : Exp, Exp
|eMul : foralle1e2 : Exp, Exp.
InductiveBexp : Type :=
|beFalse : Bexp
|beTrue : Bexp
|Neg : foralle : Bexp, Bexp
|Imp : foralle1e2 : Bexp, Bexp
|And : foralle1e2 : Bexp, Bexp
|Or : foralle1e2 : Bexp, Bexp
|Equal : foralle1e2 : Exp, Bexp
|Smaller : foralle1e2 : Exp, Bexp.
InductiveCommand : Type :=
|Skip : Command
|Seq : forallc1c2 : Command, Command
|If : forall(b : Bexp)(c1c2 : Command), Command
|While : forall(b : Bexp)(c : Command), Command
|Agn : forall(v : Var)(e : Exp), Command
|Load : forall(v : Var)(e : Exp), Command
viii
|Store : forall(e : Exp)(e : Exp), Command
|New : forall(v : Var)(e : Exp), Command
|Free : forall(v : Exp), Command.
Definition Local : Type := Var− > Value.
Definition break : Type := optionnat.
Definition sa break : sepalgbreak := sao ptionnat.
Definition mem : Type := (break ∗ (Loc− > optionnat)).
Definition LocalState : Type := (Local ∗ mem).
Definition SeqConfig : Type := (LocalState ∗ Command).
Figure 3: Definition of abstract machine structure.
Here we only explain four commands that deal with the memory and leave the other commands to the reader since they are quite standard to most of programming languages. The
command x := [e] will assign variable x with the value in the memory cell at location e. The
command [e1 ] := e2 will assign the memory cell at location e1 with the value e2 . The command
free e will free the memory cell at location e(reset it to ’None’ which is equivalent to ’not
allocated’). The command x := new e will allocate a new memory cell with the value e and
assign x with its location.
Our abstract machine has a local ρ which is a mapping from variables to values and a memory m which is a pair of an option integer called break and partial mapping from memory cells
to values. There are two important functionalities of break : on one hand, it helps to keep track
of the new allocated memory cells in which every memory cells with location greater than break
are considered to be not allocated yet, on the other hand, only the part of the memory that
holds break is allowed to execute memory operators.
There are three operators on memory : UpdateMem(m, l, v) is equivalent to m[l] := v given
that m[l] was allocated, FreeMem(m, l) will free the memory cell at location l, NewMem(m, x, e)
is equivalent to the set of actions : m[break] := e; x := break; break = break + 1 given that m
holds break.Update Mem and Free Mem will return an option memory which indicates either
the operator failed or succeeded while NewMem(,w,i)ll either return None in which the operator
failed or a pair of memory and break. Furthermore, we embed separation logic into memory,
ix
i.e. memories can be joined or split based on separation logic rules.It is proven in Coq that the
following constrains on the above three operators are obeyed:
Lemma 2 The following facts are provable:
• m1 ? m2 = m3 → m1 [l] = v → m3 [l] = v
• m[l] = Some n → ∀v, ∃m0 , UpdateMem(m, l, v) = Some m0
• m[l] = Some n → ∀v, ∃m0 , FreeMem(m, l) = Some m0
• m1 ? m2 = m3 → UpdateMem(m1 , l, v) = Some m01
→ UpdateMem(m3 , l, v) = Some m03 → m01 ? m2 = m03
• m1 ? m2 = m3 → FreeMem(m1 , l) = Some m01
→ FreeMem(m3 , l) = Some m03 → m01 ? m2 = m03
• m1 ? m2 = m3 → NewMem(m1 , x, v) = Some m01
→ NewMem(m3 , x, v) = Some m03 → m01 ? m2 = m03
A machine state is a pair of local and memory. A machine configuration is a pair of machine
state and command stack while a sequential step is a relation between two machine configurations. A complete definition of sequential step can be found in the next page.
Lemma 3 The sequential step relation is deterministic.
Proof.
Done in Coq.
Qed.
Next, we define the semantics for the Hoare tuple:
x
sstep-skip
S
Ψ ` ((ρ, m), skip • κ) 7→ ((ρ, m), κ))
ρ |= b ⇓ true
sstep-if1
S
Ψ ` ((ρ, m), if b c1 c2 • κ) 7→ ((ρ, m), c1 • κ)
ρ |= b ⇓ false
sstep-if2
S
Ψ ` ((ρ, m), if b c1 c2 • κ) 7→ ((ρ, m), c2 • κ)
ρ |= b ⇓ true
sstep-while1
S
Ψ ` ((ρ, m), while b c • κ) 7→ ((ρ, m), c • while b c • κ)
ρ |= b ⇓ false
sstep-while2
S
Ψ ` ((ρ, m), while b c • κ) 7→ ((ρ, m), κ)
sstep-seq
S
Ψ ` ((ρ, m), (c1 • c2 ) • κ) 7→ ((ρ, m), c1 • (c2 • κ))
ρ |= e1 ⇓ v1
ρ |= e2 ⇓ v2
m0 = [v1 7→ v2 ]m
sstep-store
S
Ψ ` ((ρ, m), [e1 ] := e2 • κ) 7→ ((ρ, m0 ), κ)
ρ |= e ⇓ v
n = m(v)
ρ0 = [x 7→ n]ρ
S
Ψ ` ((ρ, m), x := [e] • κ) 7→ ((ρ0 , m), κ)
sstep-load
ρ |= e ⇓ v
m.break = n
m0 = [n 7→ v]m
m0 .break = n + 1
ρ0 = [x 7→ n]ρ
sstep-new
S
Ψ ` ((ρ, m), x := new e • κ) 7→ ((ρ0 , m0 ), κ)
ρ |= e ⇓ v
m0 = [v 7→ 0]m
S
Ψ ` ((ρ, m), free e • κ) 7→ ((ρ, m0 ), κ)
sstep-free
ρ |= e ⇓ v
ρ0 = [x 7→ v]ρ
S
sstep-assign
xi ((ρ0 , m), κ)
Ψ ` ((ρ, m), x := e • κ) 7→
Figure 4: Definition of sequential relation step
S
IsSafe(config) := ∃config0 , ` config 7→ config0
Guard(P, c) := ∀st, (P ? break)st → IsSafe(st, c)
Close(P, c) := ∀v, Modify(c, v) → ∀st, Pst → ∀n, P(UpLocal st v n)
{P } c {Q} := ∀c0 F, Close(P, c) → Guard(F ? Q, c0 ) → Guard(F ? P, c; c0 )
Figure 5: Definition of Hoare tuple
IsSafe(config) guarantees that from config, we can step to somewhere else, i.e. we will
not get stuck at config.Guard(P, c) means that if the predicate P ? break holds on st, the configuration (st, c) becomes safe.Close(P, c) says that if c modifies the value of some variable v
and P holds on some state st then P still holds on st even the value of v is changed. Finally,
{P } c {Q} is defined by saying that given Close(P, c) and Guard(F ? Q, c0 ), we can imply that
Guard(F ? P, c; c0 ). Although the way the definitions are set up is not natural, it works perfectly
when we prove the correctness of the Hoare rules.
Finally, we define the Hoare rules for the set of commands and prove their correctness in
Coq with respect to the definitions :
{P } c {Q}
Frame
{F ∗ P } c {F ∗ Q}
{P1 } c {Q1 }
{P2 } c {Q2 }
Conjunction
{P1 ∧ P2 } c {Q1 ∧ Q2 }
{P } c1 {Q}
{Q} c2 {R}
Composition
{P } c1 ; c2 {R}
P → P0
{P 0 } c {Q0 }
{P } c {Q}
Q0 → Q
{emp} skip {emp}
Consequence
Skip
{P ∧ Eval(b)} c1 {Q}
{P ∧ ¬Eval(b)} c2 {Q}
If
{P } if b c1 c2 {Q}
xii
{I ∧ Eval(b)} c {I}
While
{I} while b c {I ∧ ¬Eval(b)}
where I is the loop invariant.
{e ⇓ n} x := e {x 7→ n}
Assign
{[e] 7→ e0 ∧ e ⇓ n ∧ e0 ⇓ n0 } x := [e] {x 7→ n0 ∧ [n] 7→ n0 }
{[e] 7→ −} [e] := e0 {[e] 7→ e0 }
{[e] 7→ −} free e {emp}
Load
Store
Free
{e ⇓ n} x := new e {[x] 7→ n}
New
One of the most important rules is the Frame rule : we allow ”adding” a frame predicate F
in both the pre and postcondition of a Hoare tuple. This rule is a strong reasoning tool as
it allows us to switch from local reasoning to global reasoning. The rule skip states that the
postcondition is the same as the precondition which is expectable because the command skip
does nothing. Here we restrict the precondition to be the minimal one as possible since we can
use Frame rule to extend it. Most of the rules above are standard and understandable, therefore
we would like to leave the most standard rules such as Assign, If, While to readers for self-study
and only explain the four last rules that manipulate memory.
The precondition of the Load rule states that if expression e and e0 are evaluated to n and n0
respectively and m[e] = e0 then after executing x := [e], the value of variable x will be n0 while
m[n] = n0 . The original version of this rule is simpler : {[e] 7→ e0 } x := [e] {x 7→ e0 ⇓ ∧[e] 7→ e0 }
but unsound because expression e and e0 can contains variable x. The improved Hoare tuple is
more complicated but it solves the problem.The Store rule says that if m[e] is allocated, then
xiii
running [e] := e0 will bound m[e] to e0 . Similarly, the Free rule says that if m[e] is allocated,
free e will free it. The New rule states that if in the precondition, we have expression e evaluated to value n, executing x := new e will bound x to a fresh cell address at where the value is n.
Lemma 4 The above Hoare rules are sound with respect to the Hoare tuple definition.
Proof.
Done in Coq.The proofs for those rules above are tedious and requires a few extra lemmas to
be achieved. Unfortunately, we are unable to complete the proof for the While rule, which is
considered to be the most difficult one.Hence we left it as a future work.
Qed.
0.4
Diamond: A new separation algebra
During the process of defining the semantics for the concurrent language, we developed a new
join algebra called the Diamond join.Elements in the domain of the new algebra are either
Empty, Part sh v or Full v 0 where sh is a proper share (0 < sh < 1) and v and v 0 are values.
It is noticed that the type v of Part and the type v 0 of Full need not to be the same. Then the
Diamond join algebra is axiomatized by seven axioms:
Empty ♦ Empty = Empty
Empty ♦ Part sh v = Part sh v
Part sh v ♦ Empty = Part sh v
Empty ♦ Full v = Full v
Full v ♦ Empty = Full v
sh1
sh1
L
L
sh2 = sh3 → Part sh1 v ♦ Part sh2 v = Part sh3 v
sh2 = 1 → CombineOk(v1 , v2 , v3 ) → Part sh1 v1 ♦ Part sh2 v2 = Full v3
whereCombineOk(v1 , v2 , v3 )is an arbitrary relation that satisfies four constraints:
• CombineOk(v1 , v2 , v3 ) → CombineOk(v1 , v2 , v30 ) → v3 = v30
xiv
• CombineOk(v1 , v2 , v3 ) → CombineOk(v2 , v1 , v3 )
• v1 ? v2 = v3 → CombineOk(v3 , v4 , v5 ) → ∃v, CombineOk(v1 , v, v5 ) ∧ v2 ? v4 = v
• CombineOk(v1 , v2 , v3 ) → CombineOk(v10 , v2 , v3 ) → v1 = v10
Lemma 5 The Diamond join is a model for separation logic.
We proved in Coq that the Diamond join satisfies all of the join algebra axioms and hence can
be embedded into our abstract machine memory. We believe that Diamond join can be an
elegant solution to the implementation of locks in the memory. One of the interesting features
of the new join is that it can transform a lock cell to a normal value cell and versus once the cell
has full ownership.Consequently, we are able to create/destroy a lock without declaration, i.e.
without the use of the command make lock/free lock, as long as we holds full ownership of the
cell. Hence, we can remove make lock and free lock command from the concurrent language.
0.5
0.5.1
Designing new concurrent separation logic
The study of a concurrent language with share model
In his thesis, Aquinas Hobor extended the C minor to concurrent C minor by adding five new
commands:
−
c := ...folk e (→
e )|lock e|unlock e|make lock e R|free lock e
−
−
The folk e (→
e ) command will create a new thread which calls function e on arguments →
e.
The parent thread will share a part of its world to the child thread throughout the function
parameters.In this language, locks are used as the communication among threads and each lock
is associated with an invariant R such that R must be satisfied at the time the lock is locked
as well as when it is unlocked.The make lock e command will create a a lock at location e with
the invariant R. The free lock e will turn the lock at location e back to its original value
form. The command lock e will try to lock the lock at location e. The thread that holds the
lock will have its ownership as well as the ownership of the invariant R that the lock contains.
xv
The command unlock e will unlock the lock, release its ownership as well as R.
To reason about how the ownership of the locks are shared/split/joined, a share model is
Π
implemented to embed into the memory.Hence, e 7→ v means that the cell at location e has
the value of v with a share of Π. One of the requirements for the share is that it must satisfy
the separation algebra, i.e. it must satisfy six axioms of separation algebra. There are a few
arguments about how the model should be built. One can consider a share is a positive number
in the range [0..1] where split/join is substraction/addition. Such implementation is simple
but not useful for most of the application. Another choice is to use the power set of natural
number P (N) as the domain of shares while split/join is split/join partitions of a set. However,
the shares are now not able to be split infinitely since each element in P (N) is a finite set.
Some other solutions uses mathematical knowledge such as equivalent classes and intervals to
establish the shares. Although they are considered to be elegant, it is difficult to implement
them in Coq. The chosen solution is adapted from Dockin’s[4] binary tree share model in which
is sophisticated enough for model reasoning while the size of work to implement it is reasonable.
A concurrent configuration is a triple tuple (Ω, θ̃, m). Ω is the scheduler, it is a list of
natural number that denotes the thread IDs. θ~ is a list of threads of the program. m is the
~ θi = (ρi , κi )
global memory that is shared among threads. We denote θi be the ith thread of θ.
means that θi contains a local ρi and a concurrent command stack κi . A concurrent step is a
relation between two concurrent configurations. In each step, the abstract concurrent machine
will pick the corresponding threads based on the schedular, combine the global memory with
the thread’s local and command stack to create a sequential configuration. If the command is
sequential, the thread will be run on the sequential abstract machine, otherwise, the concurrent
machine will handle the concurrent commands.
xvi
0.5.2
A new concurrent language
We extend our sequential language by adding three new commands:
c := ...lock e|unlock e|c1 ||c2
In our language, lock and unlock have the same functionality as the ones in the concurrent
C minor. The command c1 ||c2 will spawn two new threads and run c1 and c2 concurrently. The
two child threads have the same(but independent) local as the parent thread. However, after c1
and c2 are executed, those child locals would be discarded and the parent thread will continue
running with its original local. If a lock is created in the parent thread before two new threads
are spawned, each child thread will own half of the lock so that they can communicate with
each other. It is noticed that we do not include make lock and free lock in our language because
we use Diamond join to embed into the memory.
0.5.3
Bijection between start c and c1 ||c2
start c; κ means that the machine will run c and κ in parallel while c1 ||c2 ; κ will result in
running c1 and c2 parallel and then continue with κ. In the first case, a new thread in which c
is executed will be created while in the second case, two new threads are created to run c1 and
c2 . Furthermore, all new threads will start with the local in the thread that create them but
in the end, all the locals of the new threads will be abandoned. Although there are differences
in the semantics of the two above commands, we will show that they are equivalent and hence
can be replaced by the other.
Lemma 6 There is a bijection between start c; κ and c1 ||c2 ; κ. In other words, we can replace
the occurrences of one command by the other without changing the meaning of the program.
Proof.
⇒ : It is easy to see that start c; κ ≡ (c||κ); halt where halt means that the program terminates
xvii
successfully. The relation ≡ means that two control stacks have the same semantics.
⇐ : We will show that
c1 ||c2 ; κ ≡ l1 = newlock e1 ; newlock e2 ; start(lock e1 ; c1 ; unlock e1 );
start(lock e2 ; c2 ; unlock e2 ); lock e1 ; lock e2 ; κ
To see why they are equivalent, it is important to notice on the left hand side that the
commands lock e1 and lock e2 will not be executed until the two new threads created by start
release l1 and l2 . Hence, κ will only be executed after the execution of c1 and c2 .
Qed.
0.5.4
Erased concurrent step relation
In this section, we develop an erased concurrent model where many concurrency features such
as the mechanism of lock is temporarily removed.While this model is trivial for the development
of the correctness of the concurrent separation logic, it helps to demonstrate whether our concurrent language is reasonable and useful, i.e. whether meaningful programs can be be written
and validated. A full description for step relation of erased concurrent model as follow:
θ~i = (ρ, Run )
EC
~ m) 7→ (Ω, θ,
~ m)
Ψ ` (i :: Ω, θ,
er-cstep-texit
θ~i = (ρ, Run κ )
ES
Ψ ` (ρ, m, κ) 7→ (ρ0 , m0 , κ0 )
θ~0 = [i 7→ (ρ0 , Run κ0 )]θ~
~ m) EC
Ψ ` (i :: Ω, θ,
7→ (i :: Ω, θ~0 , m0 )
xviii
er-cstep-seq
θ~i = (ρ, Run lock e • κ )
ρ |= e ⇓ v
θ~0 = [i 7→ (ρ, Lock v, κ )]θ~
~ m) EC
Ψ ` (i :: Ω, θ,
7→ (Ω, θ~0 , m)
er-cstep-prelock
θ~ = (ρ, Lock v, κ )
m(v) = 0
EC
~ m) 7→ (Ω, θ,
~ m)
Ψ ` (i :: Ω, θ,
er-cstep-nolock
θ~i = (ρ, Lock v, κ )
m(v) = 1
m0 = [v 7→ 0]m
θ~0 = [i 7→ (ρ, Run κ )]θ~
EC
~ m) 7→ (i :: Ω, θ~0 , m0 )
Ψ ` (i :: Ω, θ,
er-cstep-lock
θ~i = (ρ, Run unlock e • κ )
ρ |= e ⇓ v
m0 = [v 7→ 1]m
θ~0 = [i 7→ (ρ, Run κ )]θ~
~ m) EC
Ψ ` (i :: Ω, θ,
7→ (Ω, θ~0 , m0 )
er-cstep-unlock
θ~i = (ρ, Run (c1 ||c2 ) • κ )
~ =n
|θ|
θ~0 = θ~ ++ (ρ, Run c1 • )::(ρ, Run c2 • )::nil
θ~00 = [i 7→ (ρ, Combine[(n + 1, n + 2), κ])]θ~0
~ m) EC
Ψ ` (i :: Ω, θ,
7→ (Ω, θ~00 , m0 )
xix
er-cstep-parallel
θ~i = (ρ, Combine[(j, j + 1), κ])
θ~j = (ρa , Run )
θ~j+1 = (ρb , Run )
θ~0 = [i 7→ (ρ, Run κ )]θ~
EC
~ m) 7→ (i :: Ω, θ~0 , m)
Ψ ` (i :: Ω, θ,
er-cstep-join
θ~i = (ρ, Combine[(a, b), κ])
θ~a = (ρa0 , κa00 )
κa00
θ~b = (ρb0 , κb00 )
6= (Run ) ∨ κb00 6= (Run )
EC
~ m) 7→ (Ω, θ,
~ m)
Ψ ` (i :: Ω, θ,
er-cstep-nojoin
The erased sequential relation step:
ES
er-cstep-skip
Ψ ` ((ρ, m), skip • κ) 7→ ((ρ, m), κ))
ρ |= b ⇓ true
er-cstep-if1
ES
Ψ ` ((ρ, m), if b c1 c2 • κ) 7→ ((ρ, m), c1 • κ)
ρ |= b ⇓ false
ES
er-cstep-if2
Ψ ` ((ρ, m), if b c1 c2 • κ) 7→ ((ρ, m), c2 • κ)
ρ |= b ⇓ true
er-cstep-while1
ES
Ψ ` ((ρ, m), while b c • κ) 7→ ((ρ, m), c • while b c • κ)
ρ |= b ⇓ false
ES
er-cstep-while2
Ψ ` ((ρ, m), while b c • κ) 7→ ((ρ, m), κ)
er-cstep-seq
ES
Ψ ` ((ρ, m), (c1 • c2 ) • κ) 7→ ((ρ, m), c1 • (c2 • κ))
xx
ρ |= e1 ⇓ v1
ρ |= e2 ⇓ v2
m0 = [v1 7→ v2 ]m
ES
Ψ ` ((ρ, m), [e1 ] := e2 • κ) 7→
er-cstep-store
((ρ, m0 ), κ)
ρ |= e ⇓ v
n = m(v)
ρ0 = [x 7→ n]ρ
ES
Ψ ` ((ρ, m), x := [e] • κ) 7→ ((ρ0 , m), κ)
er-cstep-load
ρ |= e ⇓ v
m.break = n
m0 = [n 7→ v]m
m0 .break = n + 1
ρ0 = [x 7→ n]ρ
er-cstep-new
ES
Ψ ` ((ρ, m), x := new e • κ) 7→ ((ρ0 , m0 ), κ)
ρ |= e ⇓ v
m0 = [v 7→ 0]m
ES
Ψ ` ((ρ, m), free e • κ) 7→ ((ρ, m0 ), κ)
er-cstep-free
ρ |= e ⇓ v
ρ0 = [x 7→ v]ρ
ES
er-cstep-assign
Ψ ` ((ρ, m), x := e • κ) 7→ ((ρ0 , m), κ)
It can be seen that for sequential commands, the erased concurrent step behaves identi cally as our sequential relation step.There are five types of concurrent command stack : Run κ
means that the thread is ready to be run with the sequential command stack κ, means that
xxi
the thread successfully terminate, Lock v, κ means that the thread is ready to execute the command lock v followed by κ, Combine[(a, b), κ] indicates that the thread is currently pending, it
can only be run κ after its two child threads θa and θb terminate.
If the chosen thread has a lock command, the pre lock step will be executed : the thread
command stack will be changed from Run κ to Lock v, κ . Then for the next time that the
thread is run, it will lock the lock v. If m(v) = 1 which means that no thread currently lock it,
the lock will be executed : the thread will lock it by changing the value of m(v) to 0. Otherwise,
if m(v) = 0 indicates some thread is using the lock, the no lock will be run, i.e. the chosen
thread needs to wait for another time.
If the chosen thread has a c1 ||c2 command, the parallel step will be executed : the abstract
concurrent machine will spawn two new threads θn = (ρ, Run c1 • ) and θn+1 = (ρ, Run c2 • )
where ρ is the local of the chosen thread. The chosen thread command stack will be changed
from Run (c1 ||c2 ) • κ to Combine[(n + 1, n + 2), κ]. For the next time the thread is run, if both
θn and θn+1 successfully terminated, the join will be executed, i.e. the thread will continue running. Otherwise, nojoin will be executed : the thread is pending and needs to wait for another
good time.
Lemma 7 The erased concurrent step relation is deterministic, i.e.
EC
EC
Ψ ` config 7→ config0 → Ψ ` config 7→ config00 → config0 = config00 .
Proof:
By doing case analysis, we proved in Coq that it is deterministic.
Qed.
xxii
0.5.5
A new look at sequential step relation
In his thesis, Aquinas Hobor[1] introduced ”lock pool” in his concurrent configuration in where
the resources associated with the resource invariant R of the locks are hidden. That is, when a
thread declares a lock (by using the command make lock), the resources of R will be temporarily
stored in the lock pool until some thread grabs the lock, the resources will be joined with the
memory of the thread. Similarly, when a thread unlocks, the resources will be moved to the
lock pool again. Unfortunately, since our language does not contain the make lock command as
well as our concurrent configuration does not include the lock pool, we need to come up with
another different method to hide/store the resources.
One of the proposed solutions is to modify the sequential step relation such that the abstract
sequential machine will have two separate memories : a main memory and an extra memory
where the resources from the main memory can be split and stored inside.We introduce a
new sequential configuration called the oracle configuration : Oconfig := (oracle, mh , (ρ, mr , c))
where oracle is a list of quarter tuple of predicates on memory, i.e. it is defined in Coq as
Definition oracle : Type := list (mpred ∗ mpred ∗ mpred ∗ mpred). Each tuple element in the oracle
specifies the resources that will be switched between mh and mr before and after each normal
sequential relation step. The oracle step is an relation between two oracle configurations. A
formal definition of oracle step is described in figure 6.
Lemma 8 The oracle relation step and the sequential relation step are equivalent, i.e. mh ? mr = mc →m0h ? m0r =
S
O
Ψ ` (ρ, mc , c) 7→ (ρ0 , m0c , c0 ) ↔ ∃o o0 , Ψ ` (o, mh , (ρ, mr , c)) 7→ (o0 , m0h (ρ0 , m0r , c0 )).
Proof.
A complete formal proof was done in Coq. Here we only explain why the lemma should hold.
’⇒’ direction : choose o = (exact mh , emp, emp, emp) :: nil where exact mh only holds on mh
and emp only holds on empty memory. Then mh will be joined into mr completely and hence
mr full = mc and m00r full = m0c . Therefore, the implication should be true.
’⇐’ direction : it is noticed that mr and m0r are submemory of mc and m0c respectively.Since the
step relation holds in mr and m0r , it should also hold in mc and m0c as well.
xxiii
mh exitpre ? mh core = mh
P1 mh exitpre
mr exitpre ? mr core = mm
P2 mr exitpre
mh exitpre ? mr core = mr full
mr exitpre ? mh core = mh full
S
Ψ ` (ρ, mr full , c) 7→ (ρ0 , m0r full , c0 )
mh exitpost ? m0h core = mh full
Q1 mh exitpost
mr exitpost ? m0r core = m0r full
Q2 mr exitpost
mh exitpost ? m0r core = m00r full
mr exitpost ? m0h core = m0h full
O
oracle step
Ψ ` ((P1 , P2 , Q1 , Q2 ) :: o, mh , (ρ, mr full , c)) 7→ (o, m0h full (ρ0 , m00r full , c0 ))
Figure 6: Definition of oracle configuration
Qed.
The problem of the above oracle step is it allows trivial cases to happen. For example, if we
choose oracle = (true, true, true, true) :: nil then the above proof also holds because the predicate
true holds on any resource. Therefore, we somehow need to set up some constraints on the
predicates to restrict the amount of transferred resources,i.e. make the transferred resources
more specific. There are two proposed approaches : either the predicates needs to be precise or
token. A predicate P is precise if it only satisfies at most one memory, is token if it is not the
true predicate. Here are formal definitions of precise and token:
token P := P ? P = false (false is predicate that does not satisfy any memory)
precise P := ∀m m0 m00 , ∃me m0e , m ? me = m0 → m ? m0e = m00 →
P m → P m 0 → m = m0 .
xxiv
With the embedded constraints on predicate, we build the precise oracle step and token
oracle step .It is easy to show that if a predicate is precise, then it is also token. However,
the converse needs not to hold. Therefore, we can consider that the precise oracle step is more
general/stronger than the token oracle step. We prove the following results in Coq:
Lemma 9 The precise oracle relation step is deterministic.
Proof.
Since there is at most one memory that each predicate can satisfy and the sequential relation
step is deterministic, we can imply that the oracle relation step is also deterministic.
Qed.
Lemma 10 The token oracle relation step is indeterministic.
Proof.
Since the token allows a predicate can satisfy more than one memory, therefore there are more
than one ways to transfer then resources.In Coq, we give a counter example for the case the
memory has only two cells.
Qed.
While the precise oracle relation step is better at controlling the amount of transferred resources, it may restrict the reasoning power of our model. On the other hand, the token oracle
relation step provides relaxed amount of transferred resources but makes the model indeterministic.Therefore, we need to build a more sophisticated model to reason about transferred
resources.
xxv
0.6
Improved mechanized list separation library
List separation is an extension of the separation logic where the separation operator is applied
to a list instead of just two elements. The original definition of list separation is based on the
FoldR operator that require an initial value for the join sum, i.e. :
L
L e s := L.1
L
L
L
(L.2 (..(L.n e)..)) = s
(7)
While in theory, the above definition seems to be completely reasonable, it becomes uncomfortable when coming into practice. The problem is that the initial value e is usually assigned
with the identity element in which it plays no role in the implementation. Therefore, we decided
to remove e from the definition. Hence, the new definition is as follow:
L
L s := L.1
L
L
L
(L.2 (..(L.(n − 1) L.n)..)) = s
(8)
In Coq, it is written as :
Fixpoint join list ‘{sepalg A}(xs : listA)(sum : A) : Prop :=
match (xs, sum) with
|(nil, sum) => Void
|(x :: nil, sum) => x = sum
|(x :: xs0 , sum) =>
exists x0 : A, join list xs0 x0 ∧ join x x0 sum
end.
The only problem with the new definition is when L is nil. There are two proposed solutions : either the operation will succeed and s is the identity element or the operator will fail.
However, the first solution is not rigorous because if the list contains only identity elements
then s is still the identity element. Therefore, we choose to stick with the second solution.
Furthermore, we proves a lot of useful facts about list separation:
xxvi
Lemma 11 The following facts are provable:
• join list L a → join list L a0 → a = a0 (deterministism)
• join list (a :: b :: L) c → ∃d, join a b d ∧ join list(d :: L) c(associtivity)
• join list (a :: L) c → join list (a0 :: L) c → a = a0 (cancellation)
• join list (a :: b :: L) c → join list (b :: a :: L) c(commutativity)
• join list L s → join s a s0 → join list (a :: L) s0
• join list (a :: L) c → join list (a0 :: L) c → a = a0 (cancellation)
• join list (a :: b :: L) c → join list (b :: a :: L) c(commutativity)
• join list L s → join s a s0 → join list (a :: L) s0
• join list (a :: L) s0 → L 6= nil → ∃s, join list L s ∧ join s a s0
• join list (a00 :: L) s → join a a0 a00 → join list (a :: a0 :: L) s
• join list L s → identity s → nth error L = Some b → idenity b
• join list (L1 ++L2 )s → join b s s0 → join list (L1 ++b :: L2 ) s0 (insertion)
• join list L1 s → Permutation L1 L2 → join list L2 s(permutation)
• join list L s → same silhouette h h0 → nth error L n = Some h →
replace L n h0 = Some L0 → join list L0 s0 → same silhouette s s0
• join list L s → join s s0 s00 → nth error L n = Some a →
∃a0 L0 , join a s0 a0 → replace L n a0 = Some L0 → join list L0 s00
• join list L s → nth error L n = Some a00 → join a a0 a00 →
∃s0 L0 , replace L n a = Some L0 → join list L0 s0 → join s0 a0 s
• join list L s → age s s0 → ∃L0 , list relageLL0 ∧ join list L0 s0
join list L0 s0 → age s s0 → ∃L, list relageLL0 ∧ join list L s
xxvii
• join list L s → nth error L n = Some a → age a a0 →
∃L0 s0 , join list L0 s0 ∧ age s s0 ∧ nth error L0 n = Some a0
• join list L0 s0 → nth error L0 n = Some a0 → age a a0 →
∃L s, join list L s ∧ age s s0 ∧ nth error L n = Some a
In the above lemma, nth error L n will return Some a if a is the nth element of L, otherwise if the operation is invalid, i.e. n is greater than the length of L then it will return
None. The replace L n a will return Some L0 which is L that has the nth element replaced by
a, otherwise it returns None. The list rel R L L0 is a proposition which is true if and only if
R (nth error L n) (nth error L0 n) for every possible natural number n ( R is a binary relation).
Here are their definitions in Coq:
Fixpoint replace(A : Type)(xs : list A)(n : nat)(a : A) : option (listA) :=
match (n, xs) with
|(0, x :: xs0 ) => Some (a :: xs0 )
|(Sn, x :: xs0 ) => match replace A xs0 n a with
|Some xs00 => Some (x :: xs00 )
|None => None
end
|( , nil) => None
end.
Fixpoint Lists rel {A : Type}(R : A− > A− > Prop)(L1 : list A)(L2 : list A) : Prop :=
match(L1, L2)with
|(nil, L2) => L2 = nil
|(L1, nil) => L1 = nil
|(x1 :: L10 , x2 :: L20 ) => R x1 x2 Lists rel R L10 L20
end.
xxviii
Fixpoint nth error(l : listA)(n : nat)struct n : Exc A :=
matchn, l with
|O, x :: => value x
|S n, :: l => nth error l n
| , => error
end.
0.7
In progress : Soundness of new Concurrent Separation Logic
With the development of Diamond join and swapped memories model, we are ready to develop
the separation concurrent model. Unfortunately, due to time constraint, we are unable to finish
it within this semester. We expect that the work can be done in a near future.
One of the challenging tasks that we have not finished yet is the correctness proof for the Hoare
While rule. The problem lies within the property of termination of the loop as well as the lack
of termination reasoning tools. One of the approaches is to apply induction on the number of
times the commands in the loop will be run. However, it is still an experiment and whether it
will succeed or not is indeterministic.
During the project, we found that resources can be lost in the concurrent model. For example,
a thread can hold the resource of the lock and then die with it. Therefore, we would like to
develop a model such that loss of resource is impossible. Such problem can be a serious research
in the future.
0.8
Conclusion
With the concept of separation logic, we presented an operational semantics for sequential
language ,developed the sequential Hoare rules and proved their correctness with respect to the
definition. Furthermore, we investigated the concurrent language by defining its operational
xxix
semantics. We also developed a new model for separation logic called the Diamond join At the
same time, we developed the operational semantics for swapped memory to reason about how a
normal memory cell can be transformed to a lock cell as well as the reverse direction.Furthemore,
we are in the progress of developing the correctness proof for concurrent separation logic. Last
but not least, our Hoare rules system can be used as a reasoning tool for both sequential and
concurrent languages.
xxx
References
[1] Aquinas Hobor.Oracle Semantics Ph.D. Thesis, Princeton TR-836-08, October 2008.
[2] P. W. OHearn.Resources, concurrency and local reasoning. Theoretical Computer Science,
375(1):271307, May 2007.
[3] A. Hobor, A. W. Appel, and F. Zappa Nardelli.Oracle semantics for concurrent separation
logic (extended version). Tech. report, Princeton University, Jan. 2008.
[4] Robert Dockins, Aquinas Hobor , Andrew W. Appel .A Fresh Look at Separation Algebras
and Share Accounting. APLAS 2009, December 2009.
[5] Aquinas Hobor, Robert Dockins, Andrew W. Appel.A Theory of Indirection via Approximation. POPL 2010, January 2010.
[6] J. C. Reynolds. Separation logic: A logic for shared mutable data structures. In LICS ’02:
Proc. of the 17th Annual IEEE Symp. on Logic in Computer Science,pages 55-74, 2002.
[7] C. A. R. Hoare. An axiomatic basis for computer programming. Communications of the
ACM, 12(10):576580,583 October 1969.
[8] C. Calcagno, P. W. O’Hearn, and H. Yang. Local action and abstract separation logic. In
LICS ’07: Proceedings of the 22nd IEEE Symp. on Logic in Computer Science, pages
366-378, 2007.
xxxi
© Copyright 2026 Paperzz