Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
(**************************************************************************)
(* *)
(* This file is part of the Frama-C's E-ACSL plug-in. *)
(* *)
(* Copyright (C) 2012 *)
(* CEA (Commissariat l'nergie atomique et aux nergies *)
(* alternatives) *)
(* *)
(* you can redistribute it and/or modify it under the terms of the GNU *)
(* Lesser General Public License as published by the Free Software *)
(* Foundation, version 2.1. *)
(* *)
(* It is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU Lesser General Public License for more details. *)
(* *)
(* See the GNU Lesser General Public License version 2.1 *)
(* for more details (enclosed in the file licenses/LGPLv2.1). *)
(* *)
(**************************************************************************)
open Cil_types
open Cil_datatype
let init_mpz () =
let set_mpzt = object
inherit Cil.nopCilVisitor
method vglob = function
| GType({ torig_name = s } as info, _) when s = "mpz_t" ->
Mpz.set_t info;
Cil.SkipChildren
| _ ->
Cil.SkipChildren
end in
Cil.visitCilFileSameGlobals set_mpzt (Ast.get ())
module Env: sig
val default_varinfos: Varinfo.Set.t option -> Varinfo.Set.t
val apply: (kernel_function -> 'a) -> kernel_function -> 'a
val clear: unit -> unit
val add: kernel_function -> Varinfo.Set.t option Stmt.Hashtbl.t -> unit
val find: kernel_function -> Varinfo.Set.t option Stmt.Hashtbl.t
module StartData: Dataflow.StmtStartData with type key = stmt
and type data = Varinfo.Set.t option
val is_consolidated: unit -> bool
val consolidate: Varinfo.Set.t -> unit
val consolidated_mem: varinfo -> bool
end = struct
let current_kf = ref (Kernel_function.dummy ())
let default_varinfos = function None -> Varinfo.Set.empty | Some s -> s
let apply f kf =
let old = !current_kf in
current_kf := kf;
let res = f kf in
current_kf := old;
res
let tbl = Kernel_function.Hashtbl.create 7
let add = Kernel_function.Hashtbl.add tbl
let find = Kernel_function.Hashtbl.find tbl
module StartData = struct
type data = Varinfo.Set.t option
type key = stmt
let apply f =
try
let h = Kernel_function.Hashtbl.find tbl !current_kf in
f h
with Not_found ->
assert false
let clear () = apply Stmt.Hashtbl.clear
let mem k = apply Stmt.Hashtbl.mem k
let find k = apply Stmt.Hashtbl.find k
let replace k v = apply Stmt.Hashtbl.replace k v
let add k v = apply Stmt.Hashtbl.add k v
let iter f = apply (Stmt.Hashtbl.iter f)
let length () = apply Stmt.Hashtbl.length
end
let consolidated_set = ref Varinfo.Set.empty
let is_consolidated_ref = ref false
let consolidate set =
let set = Varinfo.Set.fold Varinfo.Set.add set !consolidated_set in
consolidated_set := set
let consolidated_mem v =
is_consolidated_ref := true;
Kernel_function.Hashtbl.clear tbl; (* will not be used anymore *)
Varinfo.Set.mem v !consolidated_set
let is_consolidated () = !is_consolidated_ref
let clear () =
Kernel_function.Hashtbl.clear tbl;
consolidated_set := Varinfo.Set.empty;
is_consolidated_ref := false
end
let reset = Env.clear
module rec Transfer
: Dataflow.BackwardsTransfer with type t = Varinfo.Set.t option
and type StmtStartData.key = stmt
= struct
let name = "E_ACSL.Pre_analysis"
let debug = ref false
type t = Varinfo.Set.t option
module StmtStartData = Env.StartData
let pretty _fmt _state = assert false
(** The data at function exit. Used for statements with no successors.
This is usually bottom, since we'll also use doStmt on Return
statements. *)
let funcExitData = None
(** When the analysis reaches the start of a block, combine the old data with
the one we have just computed. Return None if the combination is the same
as the old data, otherwise return the combination. In the latter case, the
predecessors of the statement are put on the working list. *)
let combineStmtStartData _stmt ~old state = match old, state with
| _, None -> assert false
| None, Some _ -> Some state (* [old] already included in [state] *)
| Some old, Some new_ ->
assert (Varinfo.Set.equal old new_);
None
(** Take the data from two successors and combine it *)
let combineSuccessors s1 s2 =
Some (Varinfo.Set.union (Env.default_varinfos s1) (Env.default_varinfos s2))
let rec register_term_lval kf varinfos (thost, _) = match thost with
| TVar { lv_origin = None } -> Error.not_yet "logic variable"
| TVar { lv_origin = Some vi } ->
Varinfo.Set.add vi varinfos
| TResult _ -> Varinfo.Set.add (Misc.result_vi kf) varinfos
| TMem t -> register_term kf varinfos t
and register_term kf varinfos term = match term.term_node with
| TConst _ | TSizeOf _ | TSizeOfE _ | TSizeOfStr _ | TAlignOf _
| TAlignOfE _ | Tnull | Ttype _ ->
varinfos
| TLval tlv | TAddrOf tlv | TStartOf tlv ->
register_term_lval kf varinfos tlv
| TUnOp(_, t) | TCastE(_, t) | Tlambda(_, t) | Tat(t, _) | Tlet(_, t) ->
register_term kf varinfos t
| TBinOp(_, t1, t2) | Tif(_, t1, t2) ->
let s = register_term kf varinfos t1 in
register_term kf s t2
| Tapp(_, _, l) -> List.fold_left (register_term kf) varinfos l
| TDataCons _ -> Error.not_yet "data constructor"
| Tbase_addr _ -> Error.not_yet "\\base_addr"
| Toffset _ -> Error.not_yet "\\offset"
| Tblock_length _ -> Error.not_yet "\\block_length"
| TCoerce _ -> Error.not_yet "coerce"
| TCoerceE _ -> Error.not_yet "coerce expression"
| TUpdate _ -> Error.not_yet "functional update"
| Ttypeof _ -> Error.not_yet "typeof"
| Tempty_set -> Error.not_yet "empty set"
| Tunion _ -> Error.not_yet "set union"
| Tinter _ -> Error.not_yet "set intersection"
| Tcomprehension _ -> Error.not_yet "set comprehension"
| Trange _ -> Error.not_yet "\\range"
(** The (backwards) transfer function for a branch. The [(Cil.CurrentLoc.get
())] is set before calling this. If it returns None, then we have some
default handling. Otherwise, the returned data is the data before the
branch (not considering the exception handlers) *)
let doStmt stmt =
(* Options.feedback "ANALYSING %a" Cil.d_stmt stmt;*)
let _, kf = Kernel_function.find_from_sid stmt.sid in
let is_first =
try Stmt.equal stmt (Kernel_function.find_first_stmt kf)
with Kernel_function.No_Statement -> assert false
in
let is_last =
try Stmt.equal stmt (Kernel_function.find_return kf)
with Kernel_function.No_Statement -> assert false
in
let register state_ref = object
inherit Visitor.frama_c_inplace
method vpredicate = function
| Pvalid(_, t) | Pvalid_read(_, t) | Pinitialized(_, t)
| Pallocable(_, t) | Pfreeable(_, t) | Pfresh(_, _, t, _) ->
(* Options.feedback "REGISTER %a" Cil.d_term t;*)
state_ref := register_term kf !state_ref t;
Cil.SkipChildren
| Pseparated _ -> Error.not_yet "\\separated"
| Ptrue | Pfalse | Papp _ | Prel _
| Pand _ | Por _ | Pxor _ | Pimplies _ | Piff _ | Pnot _ | Pif _
| Plet _ | Pforall _ | Pexists _ | Pat _ | Psubtype _ ->
Cil.DoChildren
method vterm term = match term.term_node with
| Tbase_addr(_, t) | Toffset(_, t) | Tblock_length(_, t) ->
state_ref := register_term kf !state_ref t;
Cil.SkipChildren
| TConst _ | TSizeOf _ | TSizeOfStr _ | TAlignOf _ | TLval _ | Tnull
| Ttype _ | Tempty_set ->
(* no sub-term inside: skip for efficiency *)
Cil.SkipChildren
| TSizeOfE _ | TAlignOfE _ | TUnOp _ | TBinOp _ | TCastE _ | TAddrOf _
| TStartOf _ | Tapp _ | Tlambda _ | TDataCons _ | Tif _ | Tat _
| TCoerce _ | TCoerceE _ | TUpdate _ | Ttypeof _ | Tunion _ | Tinter _
| Tcomprehension _ | Trange _ | Tlet _ ->
(* potential sub-term inside *)
Cil.DoChildren
method vlogic_label _ = Cil.SkipChildren
end in
let register_predicate pred state =
let state_ref = ref state in
ignore (Visitor.visitFramacIdPredicate (register state_ref) pred);
!state_ref
in
let register_code_annot a state =
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
let state_ref = ref state in
ignore (Visitor.visitFramacCodeAnnotation (register state_ref) a);
!state_ref
in
Dataflow.Post
(fun state ->
let state = Env.default_varinfos state in
let state =
if is_first || is_last then
Annotations.fold_behaviors
(fun _ bhv s ->
let handle_annot test f s =
if test then
f (fun _ p s -> register_predicate p s) kf bhv.b_name s
else
s
in
let s = handle_annot is_first Annotations.fold_requires s in
handle_annot
is_last
(fun f -> Annotations.fold_ensures (fun e (_, p) -> f e p)) s)
kf
state
else
state
in
let state =
Annotations.fold_code_annot (fun _ -> register_code_annot) stmt state
in
Some state)
let rec register_exp varinfos e = match e.enode with
| Lval lv | AddrOf lv | StartOf lv ->
(match lv with
| Var vi, _ -> Varinfo.Set.add vi varinfos
| Mem e, _ -> register_exp varinfos e)
| UnOp(_, e, _) | CastE(_, e) | Info(e, _) -> register_exp varinfos e
| BinOp(_, e1, e2, _) ->
let s = register_exp varinfos e1 in
register_exp s e2
| Const _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _ | AlignOfE _ ->
varinfos
let base_addr e =
let rec aux acc e = match e.enode with
| Lval lv | AddrOf lv | StartOf lv ->
(match lv with
| Var vi, _ -> [ vi ]
| Mem e, _ -> aux acc e)
| BinOp((PlusPI | IndexPI | MinusPI), e1, _, _) -> aux acc e1
| BinOp(MinusPP, e1, e2, _) ->
let acc = aux acc e1 in
aux acc e2
| Info(e, _) | CastE(_, e) -> aux acc e
| BinOp((PlusA | MinusA | Mult | Div | Mod |Shiftlt | Shiftrt
| Lt | Gt | Le | Ge | Eq | Ne | BAnd | BXor | BOr | LAnd | LOr),
_, _, _)
| UnOp _ | Const _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _
| AlignOfE _ -> assert false
in
aux [] e
(** The (backwards) transfer function for an instruction. The
[(Cil.CurrentLoc.get ())] is set before calling this. If it returns
None, then we have some default handling. Otherwise, the returned data is
the data before the branch (not considering the exception handlers) *)
let doInstr _stmt instr state =
let state = Env.default_varinfos state in
let extend_to_expr state lhost e =
let add_vi state vi =
if Varinfo.Set.mem vi state then register_exp state e
else state
in
match lhost with
| Var vi -> add_vi state vi
| Mem e ->
let l = base_addr e in
List.fold_left add_vi state l
in
match instr with
| Set((lhost, _), e, _) ->
Dataflow.Done (Some (extend_to_expr state lhost e))
| Call(result, f_exp, l, _) ->
(match f_exp.enode with
| Lval(Var vi, NoOffset) ->
let kf = Globals.Functions.get vi in
let state =
if Kernel_function.is_definition kf then
let state_kf = Compute.get kf in
(* keep arguments whenever the corresponding formals must be kept *)
try
List.fold_left2
(fun acc p a ->
if Varinfo.Set.mem p state_kf then
extend_to_expr acc (Var p) a
else
acc)
state
(Globals.Functions.get_params kf)
l
with Invalid_argument _ ->
Error.not_yet "variadic function call"
else
state
in
let state = match result with
| None -> state
| Some (lhost, _) ->
(* result of this call must be kept; so keep each argument *)
List.fold_left (fun acc e -> extend_to_expr acc lhost e) state l
in
Dataflow.Done (Some state)
| _ ->
(* imprecise function call: keep each argument *)
Dataflow.Done
(Some (List.fold_left (fun acc e -> register_exp acc e) state l)))
| Asm _ -> Error.not_yet "asm"
| Skip _ | Code_annot _ -> Dataflow.Default
(** Whether to put this statement in the worklist. This is called when a
block would normally be put in the worklist. *)
let filterStmt _predecessor _block = true
(** Must return [true] if there is a path in the control-flow graph of the
function from the first statement to the second. Used to choose a "good"
node in the worklist. Suggested use is [let stmt_can_reach =
Stmts_graph.stmt_can_reach kf], where [kf] is the kernel_function
being analyzed; [let stmt_can_reach _ _ = true] is also correct,
albeit less efficient *)
let stmt_can_reach stmt =
let _, kf = Kernel_function.find_from_sid stmt.sid in
Stmts_graph.stmt_can_reach kf stmt
end
and Compute: sig val get: kernel_function -> Varinfo.Set.t end = struct
module D = Dataflow.Backwards(Transfer)
let compute kf =
(* Options.feedback "ANALYSING %a" Kernel_function.pretty kf;*)
let tbl = Stmt.Hashtbl.create 17 in
Env.add kf tbl;
try
let init = object
inherit Visitor.frama_c_inplace
method vstmt_aux stmt =
Stmt.Hashtbl.add tbl stmt None;
Cil.DoChildren
method vinst _ = Cil.SkipChildren
method vexpr _ = Cil.SkipChildren
end
in
let fundec = Kernel_function.get_definition kf in
let stmt = Kernel_function.find_return kf in
ignore (Visitor.visitFramacFunction init fundec);
D.compute [ stmt ];
tbl
with Kernel_function.No_Definition | Kernel_function.No_Statement ->
tbl
let get kf =
try
let stmt = Kernel_function.find_first_stmt kf in
(* Options.feedback "GETTING %a" Kernel_function.pretty kf;*)
let tbl = try Env.find kf with Not_found -> Env.apply compute kf in
try
let set = Stmt.Hashtbl.find tbl stmt in
Env.default_varinfos set
with Not_found ->
Options.fatal "stmt never analyzed: %a" Cil.d_stmt stmt
with Kernel_function.No_Statement ->
Varinfo.Set.empty
end
let must_model_vi ?kf vi =
if Env.is_consolidated () then
Env.consolidated_mem vi
else
match kf with
| None ->
Globals.Functions.iter
(fun kf ->
let set = Compute.get kf in
Env.consolidate set);
(* Options.feedback "MUST MODEL %s? %b (consolidated)"
vi.vname (Env.consolidated_mem vi);*)
Env.consolidated_mem vi
| Some kf ->
let set = Compute.get kf in
(* Options.feedback "MUST MODEL %s? %b" vi.vname (Varinfo.Set.mem vi set);*)
Varinfo.Set.mem vi set
let rec must_model_lval kf = function
| Var vi, _ -> must_model_vi ~kf vi
| Mem e, _ -> must_model_exp kf e
and must_model_exp kf e = match e.enode with
| Lval lv | AddrOf lv | StartOf lv -> must_model_lval kf lv
| BinOp((PlusPI | IndexPI | MinusPI), e1, _, _) -> must_model_exp kf e1
| BinOp(MinusPP, e1, e2, _) -> must_model_exp kf e1 || must_model_exp kf e2
| Info(e, _) | CastE(_, e) -> must_model_exp kf e
| BinOp((PlusA | MinusA | Mult | Div | Mod |Shiftlt | Shiftrt
| Lt | Gt | Le | Ge | Eq | Ne | BAnd | BXor | BOr | LAnd | LOr),
_, _, _)
| UnOp _ | Const _ | SizeOf _ | SizeOfE _ | SizeOfStr _ | AlignOf _
| AlignOfE _ -> assert false
(*
Local Variables:
compile-command: "make"
End:
*)