2 (** Schorr-Waite algorithm
4 The Schorr-Waite algorithm is the first mountain that any
5 formalism for pointer aliasing should climb.
6 -- Richard Bornat, 2000
8 Author: Mário Pereira (UBI, then Université Paris Sud)
10 The code, specification, and invariants below follow those of
11 the following two proofs:
13 - Thierry Hubert and Claude Marché, using Caduceus and Coq
15 A case study of C source code verification: the Schorr-Waite algorithm.
16 SEFM 2005. http://www.lri.fr/~marche/hubert05sefm.ps
18 - Rustan Leino, using Dafny
20 Dafny: An Automatic Program Verifier for Functional Correctness.
22 http://research.microsoft.com/en-us/um/people/leino/papers/krml203.pdf
36 (** Why3 has no support for arbitrary pointers,
37 so we introduce a small component-as-array memory model *)
39 (** the type of pointers and the null pointer *)
41 val constant null: loc
43 val (=) (a b: loc) : bool
44 ensures { result = (a = b) }
46 (** each (non-null) location holds four fields: two Boolean marks m and c
47 and two pointers left and right *)
48 val m: ref (map loc bool)
49 val c: ref (map loc bool)
50 val left: ref (map loc loc)
51 val right: ref (map loc loc)
53 val get_left (p: loc) : loc
54 requires { p <> null }
55 ensures { result = !left[p] }
57 val set_left (p: loc) (v: loc) : unit
58 requires { p <> null }
60 ensures { !left = set (old !left) p v }
62 val get_right (p: loc) : loc
63 requires { p <> null }
64 ensures { result = !right[p] }
66 val set_right (p: loc) (v: loc) : unit
67 requires { p <> null }
69 ensures { !right = set (old !right) p v }
71 val get_m (p: loc) : bool
72 requires { p <> null }
73 ensures { result = !m[p] }
75 val set_m (p: loc) (v: bool) : unit
76 requires { p <> null }
78 ensures { !m = set (old !m) p v }
80 val get_c (p: loc) : bool
81 requires { p <> null }
82 ensures { result = !c[p] }
84 val set_c (p: loc) (v: bool) : unit
85 requires { p <> null }
87 ensures { !c = set (old !c) p v }
89 (** for the purpose of the proof, we add a fifth, ghost, field,
90 which records the path from the root (when relevant) *)
91 val ghost path_from_root : ref (map loc (list loc))
93 val ghost get_path_from_root (p : loc) : list loc
94 ensures { result = !path_from_root[p] }
96 val ghost set_path_from_root (p: loc) (l : list loc) : unit
97 requires { p <> null }
98 writes { path_from_root }
99 ensures { !path_from_root = set (old !path_from_root) p l }
101 (** Stack of nodes, from the root to the current location, in
102 reverse order (i.e. the current location is the first element
105 type stacknodes = Seq.seq loc
107 predicate not_in_stack (n: loc) (s: stacknodes) =
108 forall i: int. 0 <= i < Seq.length s -> n <> Seq.get s i
110 let ghost tl_stackNodes (stack: stacknodes) : stacknodes
111 requires { Seq.length stack > 0 }
112 ensures { result = stack[1..] }
113 ensures { forall n. not_in_stack n stack -> not_in_stack n result }
116 (** two lemmas about stacks *)
118 let lemma cons_not_in (s: stacknodes) (n t: loc)
119 requires { not_in_stack n (cons t s) }
120 ensures { not_in_stack n s }
122 assert { forall i: int. 0 <= i < Seq.length s ->
123 Seq.get s i = Seq.get (cons t s) (i+1) }
125 let lemma tl_cons (s1 s2: stacknodes) (p: loc)
126 requires { Seq.length s2 > 0 }
127 requires { s1 = s2[1..] }
128 requires { p = Seq.get s2 0 }
129 ensures { s2 = cons p s1 }
130 = assert { Seq.(==) s2 (cons p s1) }
132 function last (s: stacknodes) : loc =
133 Seq.get s (Seq.length s - 1)
135 predicate distinct (s: stacknodes) =
136 forall i j. 0 <= i < Seq.length s -> 0 <= j < Seq.length s -> i <> j ->
137 Seq.get s i <> Seq.get s j
141 predicate edge (x y : loc) (left right : map loc loc) =
142 x <> null /\ (left[x] = y \/ right[x] = y)
144 inductive path (left right : map loc loc) (x y : loc) (p : list loc) =
145 | path_nil : forall x : loc, l r : map loc loc. path l r x x Nil
146 | path_cons : forall x y z : loc,
149 edge x z l r -> path l r z y p ->
150 path l r x y (Cons x p)
152 let rec lemma trans_path (x y z : loc) (l r : map loc loc) (p1 p2 : list loc)
153 variant { length p1 }
154 requires { path l r x y p1 }
155 requires { path l r y z p2 }
156 ensures { path l r x z (p1 ++ p2) }
158 | Cons _ (Cons b _ as p') -> trans_path b y z l r p' p2
162 lemma path_edge : forall x y : loc, left right : map loc loc.
163 edge x y left right -> path left right x y (Cons x Nil)
165 lemma path_edge_cons:
166 forall n x y : loc, left right : map loc loc, pth : list loc.
167 path left right n x pth -> edge x y left right ->
168 path left right n y (pth ++ (Cons x Nil))
170 predicate reachable (left right: map loc loc) (x y : loc) =
171 exists p : list loc. path left right x y p
173 (** Schorr-Waite algorithm *)
175 let schorr_waite (root: loc) (ghost graph : S.fset loc) : unit
176 requires { root <> null /\ S.mem root graph }
177 (* graph is closed under left and right *)
178 requires { forall n : loc. S.mem n graph ->
180 (!left[n] = null \/ S.mem !left[n] graph) /\
181 (!right[n] = null \/ S.mem !right[n] graph) }
182 (* graph starts with nothing marked *)
183 requires { forall x : loc. S.mem x graph -> not !m[x] }
184 (* the structure of the graph is not changed *)
185 ensures { forall n : loc. S.mem n graph ->
186 (old !left)[n] = !left[n] /\
187 (old !right)[n] = !right[n] }
188 (* all the non-null vertices reachable from root
189 are marked at the end of the algorithm, and only these *)
190 ensures { forall n : loc. S.mem n graph -> !m[n] ->
191 reachable (old !left) (old !right) root n }
193 ensures { forall n : loc. S.mem n graph -> !m[n] ->
194 (forall ch : loc. edge n ch !left !right -> ch <> null -> !m[ch]) }
195 = let t = ref root in
197 let ghost stackNodes = ref Seq.empty in
198 let ghost pth = ref Nil in
199 ghost set_path_from_root !t !pth;
200 let ghost unmarked_nodes = ref graph in
201 let ghost c_false_nodes = ref graph in
202 while !p <> null || (!t <> null && not get_m !t) do
203 invariant { forall n. S.mem n graph -> not_in_stack n !stackNodes \/
204 exists i : int. Seq.get !stackNodes i = n }
205 invariant { not_in_stack null !stackNodes }
206 invariant { Seq.length !stackNodes = 0 <-> !p = null }
207 invariant { !p <> null -> S.mem !p graph }
208 invariant { Seq.length !stackNodes <> 0 -> Seq.get !stackNodes 0 = !p }
209 invariant { forall n : loc. S.mem n graph -> not !m[n] ->
210 S.mem n !unmarked_nodes }
211 invariant { forall n : loc. S.mem n graph -> not !c[n] ->
212 S.mem n !c_false_nodes }
213 invariant { forall i. 0 <= i < Seq.length !stackNodes ->
214 S.mem (Seq.get !stackNodes i) graph }
215 invariant { forall i. 0 <= i < Seq.length !stackNodes - 1 ->
216 let p1 = Seq.get !stackNodes i in
217 let p2 = Seq.get !stackNodes (i+1) in
218 (!c[p2] -> old !left[p2] = !left[p2] /\
219 old !right[p2] = p1) /\
220 (not !c[p2] -> old !left[p2] = p1 /\
221 old !right[p2] = !right[p2]) }
222 invariant { !path_from_root[root] = Nil }
223 invariant { forall n : loc. S.mem n graph ->
224 not_in_stack n !stackNodes ->
225 !left[n] = old !left[n] /\
226 !right[n] = old !right[n] }
227 (* something like Leino's line 74; this is useful to prove that
228 * the stack is empty iff p = null *)
229 invariant { Seq.length !stackNodes <> 0 ->
230 let first = last !stackNodes in
231 if !c[first] then !right[first] = null
232 else !left[first] = null }
233 invariant { Seq.length !stackNodes <> 0 -> last !stackNodes = root }
234 (* something like lines 75-76 from Leino's paper *)
235 invariant { forall k : int. 0 <= k < Seq.length !stackNodes - 1 ->
236 if !c[Seq.get !stackNodes k]
237 then !right[Seq.get !stackNodes k] = Seq.get !stackNodes (k+1)
238 else !left [Seq.get !stackNodes k] = Seq.get !stackNodes (k+1) }
239 (* all nodes in the stack are marked
240 * (I4a in Hubert and Marché and something alike line 57 in Leino) *)
241 invariant { forall i. 0 <= i < Seq.length !stackNodes ->
242 !m[Seq.get !stackNodes i] }
243 (* stack has no duplicates ---> line 55 from Leino's paper *)
244 invariant { distinct !stackNodes }
245 (* something like Leino's line 68 *)
246 invariant { forall i. 0 <= i < Seq.length !stackNodes ->
247 let n = Seq.get !stackNodes i in
248 if !c[n] then !left[n] = old !left[n]
249 else !right[n] = old !right[n] }
250 (* lines 80-81 from Leino's paper *)
251 invariant { Seq.length !stackNodes <> 0 ->
252 if !c[!p] then old !right[!p] = !t
253 else old !left[!p] = !t }
254 (* lines 78-79 from Leino's paper *)
255 invariant { forall k : int. 0 < k < Seq.length !stackNodes ->
256 let n = Seq.get !stackNodes k in
258 then Seq.get !stackNodes (k - 1) = old !right[n]
259 else Seq.get !stackNodes (k - 1) = old !left[n] }
260 (* line 70 from Leino's paper *)
261 invariant { !p <> null ->
262 path (old !left) (old !right) root !p !pth }
263 (* line 72 from Leino's paper *)
264 invariant { forall n : loc. S.mem n graph -> !m[n] ->
265 reachable (old !left) (old !right) root n }
266 invariant { !p = null -> !t = root }
267 (* line 70 from Leino's paper *)
268 invariant { forall n : loc, pth : list loc.
269 S.mem n graph -> !m[n] ->
270 pth = !path_from_root[n] ->
271 path (old !left) (old !right) root n pth }
272 (* lines 61-62 from Leinos' paper *)
273 invariant { forall n : loc. S.mem n graph -> n <> null -> !m[n] ->
274 not_in_stack n !stackNodes ->
275 (!left[n] <> null -> !m[!left[n]]) /\
276 (!right[n] <> null -> !m[!right[n]]) }
277 (* something like Leino's lines 57-59 *)
278 invariant { forall i. 0 <= i < Seq.length !stackNodes ->
279 let n = Seq.get !stackNodes i in
281 (!left[n] <> null -> !m[!left[n]]) /\
282 (!right[n] <> null -> !m[!right[n]]) }
283 (* termination proved using lexicographic order over a triple *)
284 variant { S.cardinal !unmarked_nodes,
285 S.cardinal !c_false_nodes,
286 Seq.length !stackNodes }
287 if !t = null || get_m !t then begin
288 if get_c !p then begin (* pop *)
291 ghost stackNodes := tl_stackNodes !stackNodes;
294 ghost pth := get_path_from_root !p;
295 end else begin (* swing *)
298 set_right !p (get_left !p);
300 ghost c_false_nodes := S.remove !p !c_false_nodes;
303 end else begin (* push *)
306 ghost stackNodes := Seq.cons !p !stackNodes;
310 ghost if !p <> root then pth := !pth ++ (Cons q Nil) else pth := Nil;
311 ghost set_path_from_root !p !pth;
312 assert { !p = Seq.get !stackNodes 0 };
313 assert { path (old !left) (old !right) root !p !pth };
315 ghost c_false_nodes := S.add !p !c_false_nodes;
316 ghost unmarked_nodes := S.remove !p !unmarked_nodes