Source file meta_annotate.ml
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
223
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
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
open Meta_utils
open Meta_options
open Meta_parse
open Meta_dispatch
open Cil_types
let after_present p =
let vis = object (_)
inherit Visitor.frama_c_inplace
val mutable found = false
method get () = found
method! vlogic_label = function
| FormalLabel "After" -> found <- true; Cil.DoChildren
| _ -> Cil.DoChildren
end in
ignore (Visitor.visitFramacPredicate (vis :> Visitor.frama_c_visitor) p);
vis # get ()
let replace_after_before is_after stmt pred =
let vis = object (_)
inherit Visitor.frama_c_inplace
method! vlogic_label = function
| FormalLabel "After" when is_after ->
Cil.ChangeTo (BuiltinLabel Here)
| FormalLabel "Before" when is_after ->
Cil.ChangeTo (StmtLabel (ref stmt))
| FormalLabel "Before" ->
Cil.ChangeTo (BuiltinLabel Here)
| _ -> Cil.DoChildren
end in
Visitor.visitFramacPredicate vis pred
let label_counter = ref 0
let add_label stmt =
let loc = Current_loc.get () in
let label_name = "meta_pre_" ^ (string_of_int !label_counter) in
if stmt.labels = [] then (
stmt.labels <- (Label (label_name, loc, false)) :: stmt.labels ;
label_counter := !label_counter + 1
)
let delayed_instances = Stmt_Hashtbl.create 256
let delayed_contracts = Fundec_Hashtbl.create 256
let cfg_recomputation = ref Fundec_Set.empty
class context_visitor flags all_mp table = object(self)
inherit Visitor.frama_c_inplace
val mutable mp_todo = []
val skip_to_add = Stmt_Hashtbl.create 50
method fill_todo fn =
let todo = find_hash_list Str_Hashtbl.find_opt table fn in
mp_todo <- List.map (Hashtbl.find all_mp) todo
method! vfunc f =
self # fill_todo f.svar.vname;
if mp_todo = [] then Cil.SkipChildren
else (
Stmt_Hashtbl.clear skip_to_add;
Cil.DoChildren
)
method private curr_func_cfg_recompute () =
let f = Option.get self#current_func in
cfg_recomputation := Fundec_Set.add f !cfg_recomputation
method private create_next_stmt stmt =
self#curr_func_cfg_recompute ();
let loc = Current_loc.get () in
let new_stmt = Cil.mkStmtOneInstr ~valid_sid:true (Skip loc) in
add_to_hash_list Stmt_Hashtbl.(find_opt, replace) skip_to_add stmt new_stmt;
new_stmt
method! vblock _ = Cil.DoChildrenPost (fun block ->
let rec aux = function
| [] -> []
| s :: t ->
s :: (find_hash_list Stmt_Hashtbl.find_opt skip_to_add s) @ (aux t)
in block.bstmts <- aux block.bstmts;
block
)
method instantiate ?(post=None) ?(force_after=false) stmt assoc_list ump =
let loc = Property.location ump.ump_ip in
let kf = Option.get self # current_kf in
let vf = Kernel_function.get_vi kf in
let func_ptr = Cil_builder.Exp.(cil_term ~loc (addr (var vf))) in
let func_assoc = "\\func", RepVariable func_ptr in
let assoc_list = func_assoc :: assoc_list in
if not stmt.ghost then
let pred = ump.ump_property kf ~stmt assoc_list in
let post_pred = match post with
| None -> pred
| Some f -> f pred
in
if not @@ Logic_utils.is_trivially_true post_pred then
let after_pres = after_present post_pred in
let will_be_after = force_after || after_pres in
if after_pres then add_label stmt;
let annot_stmt = if will_be_after then
self # create_next_stmt stmt else stmt
in
let labelled = replace_after_before will_be_after stmt post_pred in
let finalize () =
let named = Meta_dispatch.name_mp_pred flags labelled ump.ump_counter in
let kind = if ump.ump_assert then Assert else Check in
let annot =
Logic_const.(
new_code_annotation
(AAssert ([], toplevel_predicate ~kind named)))
in
Annotations.add_code_annot ump.ump_emitter ~kf annot_stmt annot;
let ips = Property.ip_of_code_annot kf annot_stmt annot in
Meta_dispatch.add_dependency ump ips
in
add_to_hash_list Stmt_Hashtbl.(find_opt, replace) delayed_instances
stmt finalize
end
let get_callsites_ips kf rips =
let calls = Kernel_function.find_syntactic_callsites kf in
match calls with
| [] -> []
| _ ->
let segmented =
List.map
(fun rip ->
Statuses_by_call.setup_precondition_proxy kf rip;
List.map
(fun (_, stmt) ->
Statuses_by_call.precondition_at_call kf rip stmt)
calls)
rips
in
List.concat segmented
class weak_inv_visitor flags full_table table (pre, post) = object (self)
inherit context_visitor flags full_table table
method add_to_contract ump =
let kf = Option.get self # current_kf in
let pred = ump.ump_property kf [] in
let finalize () =
let pred' = Meta_dispatch.name_mp_pred flags pred ump.ump_counter in
let pred_name = (Emitter.get_name ump.ump_emitter) :: pred'.pred_name in
let pred'' = {pred' with pred_name} in
let kind = if ump.ump_assert then Assert else Check in
let deps = ref [] in
if pre then (
let ip_requires = Logic_const.new_predicate ~kind pred'' in
Annotations.add_requires ump.ump_emitter kf [ip_requires];
let defb = Option.get (Cil.find_default_behavior (Annotations.funspec kf)) in
let rips = Property.ip_of_requires kf Kglobal defb ip_requires :: !deps in
let callsites_ip = get_callsites_ips kf rips in
deps := rips @ callsites_ip @ !deps
);
begin match post with
| `None -> ()
| `Strict ->
let ip_ensures = Logic_const.new_predicate ~kind pred'' in
Annotations.add_ensures ump.ump_emitter kf [(Normal, ip_ensures)];
let defb = Option.get (Cil.find_default_behavior (Annotations.funspec kf)) in
let eip = Property.ip_of_ensures kf Kglobal defb (Normal, ip_ensures) in
deps := eip :: !deps
| `Conditional ->
let ip_assumes = Logic_const.new_predicate ~kind pred'' in
let ip_ensures = Logic_const.new_predicate ~kind pred'' in
let bh = {b_name = List.hd pred_name; b_requires = [];
b_assumes = [ip_assumes]; b_post_cond = [(Normal, ip_ensures)];
b_assigns = WritesAny; b_allocation = FreeAllocAny;
b_extended = []} in
Annotations.add_behaviors ump.ump_emitter kf [bh];
end;
Meta_dispatch.add_dependency ump !deps
in
let fundec = Kernel_function.get_definition kf in
add_to_hash_list Fundec_Hashtbl.(find_opt, replace) delayed_contracts
fundec finalize
method! vfunc f =
self # fill_todo f.svar.vname;
List.iter (self # add_to_contract) mp_todo;
Cil.SkipChildren
end
let addr_of_tlval tlval =
let typ = Cil.typeOfTermLval tlval in
Logic_utils.mk_logic_AddrOf tlval typ
let addr_of_tlhost (h,_) = addr_of_tlval (h, TNoOffset)
class calling_visitor flags all_mp table = object (self)
inherit context_visitor flags all_mp table
method calling_instance stmt called called_kf args =
let addr_term = addr_of_tlval called in
let main_assoc = ("\\called", RepVariable addr_term) in
let assoc_list = match called_kf with
| None ->
Self.warning ~once:true
"\\called_arg cannot be used with indirect calls" ;
[main_assoc]
| Some kf ->
let formals = Kernel_function.get_formals kf in
try
let param_assocs = List.map2 (fun formal given ->
let given_term = Logic_utils.expr_to_term given in
(formal.vorig_name, given_term)
) formals args
in [main_assoc; ("\\called_arg", RepApp param_assocs)]
with Invalid_argument _ ->
Self.warning ~once:true "\\called_arg cannot be used with variadic \
functions. Please use the Instantiate plugin" ;
[main_assoc]
in
List.iter (self # instantiate stmt assoc_list) mp_todo
method! vinst = function
| Call (_, fexpr, args, _) ->
let lv = match fexpr.enode with Lval lv -> lv | _ -> assert false in
let stmt = Option.get self # current_stmt in
let called_kf = Kernel_function.get_called fexpr in
let tlval = Logic_utils.lval_to_term_lval lv in
self#calling_instance stmt tlval called_kf args ;
Cil.DoChildren
| _ -> Cil.SkipChildren
end
let inspect_contract process stmt ckf args =
let vis = object (_)
inherit Visitor.frama_c_inplace
val fargs =
let h = Hashtbl.create (List.length args) in
let _, oargs, _, _ = Cil.splitFunctionType
(Kernel_function.get_type ckf) in
if (List.length args) != 0 then
List.iter2 (fun (a, _, _) b -> Hashtbl.add h a b)
(Option.get oargs) args ;
h
method! vterm _ =
Cil.DoChildrenPost (fun term -> match term.term_node with
| TLval (host, _) | TAddrOf (host, _) | TStartOf (host, _) ->
begin match host with
| TVar lovar ->
if Hashtbl.mem fargs lovar.lv_name then
Logic_utils.expr_to_term @@
Hashtbl.find fargs lovar.lv_name
else term
| _ -> term
end
| _ -> term
)
end in
let f b =
let ass =
Annotations.fold_assumes
(fun _ p old ->
(Visitor.visitFramacPredicate vis
(Logic_const.pred_of_id_pred p))
::old)
ckf b.b_name []
in
match b.b_assigns with
| WritesAny ->
Self.warning "Cannot analyze footprint of function %a: no assigns \
clause. Assuming meta-properties with reading/writing contexts are \
valid in this function." Kernel_function.pretty ckf
| Writes l -> process ckf stmt vis l ass
in
Annotations.iter_behaviors f ckf
class writing_visitor flags all_mp table = object(self)
inherit context_visitor flags all_mp table
method writing_instance ?post stmt = function
| TVar lv, _ when flags.simpl && Meta_simplify.is_not_orig_variable lv -> ()
| written ->
let addr_term = addr_of_tlval written in
let base_addr_term = addr_of_tlhost written in
let assoc_list =
[ "\\written", RepVariable addr_term;
"\\lhost_written", RepVariable base_addr_term
]
in
List.iter (self # instantiate ~post stmt assoc_list) mp_todo
method private external_assigns _ stmt vis l preconds =
List.iter (fun (it, _) ->
let term = it.it_content in
let propag = Visitor.visitFramacTerm vis term in
if not @@ Logic_utils.contains_result propag then
match propag.term_node with
| TLval t ->
let post prop =
List.fold_left (fun prop prec ->
Logic_const.pimplies (prec, prop)
) prop preconds
in
self#writing_instance ~post stmt t ;
| _ -> assert false
) l
method! vstmt_aux stmt =
if not stmt.ghost then (
match stmt.skind with
| Instr Set (lval, _, _) ->
let tl = Logic_utils.lval_to_term_lval lval in
self # writing_instance stmt tl ;
Cil.SkipChildren
| Instr Call (lval, fexp, args, (source,_)) ->
if Option.is_some lval then (
let tl = Logic_utils.lval_to_term_lval
(Option.get lval) in
self#writing_instance stmt tl
) ;
(match Kernel_function.get_called fexp with
| Some kf ->
let caller = Option.get self#current_kf in
let callee_def = Kernel_function.has_definition kf in
let is_check_assigns f =
Kernel_function.Set.mem f flags.check_callee_assigns
in
if (not callee_def && flags.check_external) ||
is_check_assigns kf && not (is_check_assigns caller)
then
inspect_contract self#external_assigns stmt kf args
| None ->
Self.warning ~source
"Indirect call '%a': assuming no 'writing' context-related \
assertions are needed"
Printer.pp_exp fexp;
); Cil.SkipChildren
| _ -> Cil.DoChildren
) else Cil.SkipChildren
end
module TLvalSet = Cil_datatype.Term_lval.Set
module StmtHashtbl = Cil_datatype.Stmt.Hashtbl
class reading_visitor flags all_mp table = object (self)
inherit context_visitor flags all_mp table
method reading_instance ?post stmt read =
let addr_term = addr_of_tlval read in
let base_addr_term = addr_of_tlhost read in
let assoc_list =
["\\read", RepVariable addr_term;
"\\lhost_read", RepVariable base_addr_term ]
in
List.iter (self#instantiate ~post stmt assoc_list) mp_todo
val lvals_by_stmt = StmtHashtbl.create 20
val mutable in_exp = false
method! vexpr e =
in_exp <- true ;
match e.enode with
| SizeOfE _ | AlignOfE _ ->
in_exp <- false; Cil.SkipChildren
| AddrOf (_,o) | StartOf(_,o) ->
ignore (Visitor.visitFramacOffset (self:>Visitor.frama_c_visitor) o);
in_exp <- false;
Cil.SkipChildren
| _ ->
Cil.DoChildrenPost (fun e -> in_exp <- false ; e)
method! vlval lval =
let typ = Cil.typeOfLval lval in
if in_exp && not (Cil.isFunctionType typ) then begin
if Option.is_some (self # current_stmt) then
let stmt = Option.get (self # current_stmt) in
begin match stmt.skind with
| UnspecifiedSequence _ -> ()
| _ ->
let tl = Logic_utils.lval_to_term_lval lval in
let old_set =
match StmtHashtbl.find_opt lvals_by_stmt stmt with
| Some s -> s
| None -> TLvalSet.empty
in
StmtHashtbl.add lvals_by_stmt stmt @@ TLvalSet.add tl old_set
end
end ; Cil.DoChildren
method passthrough_from kf stmt vis l preconds =
List.iter (fun (_, deps) -> match deps with
| FromAny ->
Self.warning ~once:true
"Cannot fully analyze read footprint of function %a: no \\from \
part in some assigns clauses."
Kernel_function.pretty kf
| From itl -> List.iter (fun it ->
let term = it.it_content in
let propag = Visitor.visitFramacTerm vis term in
match propag.term_node with
| TCast (false, _, {term_node = TLval t})
| TLval t ->
let post property =
List.fold_left (fun prop prec ->
Logic_const.pimplies (prec, prop)
) property preconds
in
self#reading_instance ~post stmt t
| _ -> ()
) itl
) l
method passthrough_process stmt kf args =
let cond =
(not (Kernel_function.has_definition kf) && flags.check_external) ||
Kernel_function.Set.(
mem kf flags.check_callee_assigns &&
not (mem (Option.get self#current_kf) flags.check_callee_assigns))
in
if cond then
inspect_contract self # passthrough_from stmt kf args
method! vstmt_aux stmt =
if not stmt.ghost then (
begin match stmt.skind with
| Instr Local_init (_, ConsInit (f, args, Plain_func), _) ->
let kf = Globals.Functions.get f in
self#passthrough_process stmt kf args
| Instr Call (_, fexp, args, (source,_)) -> begin match
Kernel_function.get_called fexp with
| Some ckf -> self#passthrough_process stmt ckf args
| None ->
Self.warning ~source
"Indirect call '%a': assuming no 'reading' context related \
assertion are needed"
Printer.pp_exp fexp
end
| _ -> ()
end ;
let after s =
let set = StmtHashtbl.find_opt lvals_by_stmt s in
if Option.is_some set then
TLvalSet.iter (self#reading_instance s) @@ Option.get set
; s
in
Cil.DoChildrenPost after
) else Cil.DoChildren
end
let lvals_of_predicate pred =
let s = ref (Some TLvalSet.empty) in
let vis = object(self)
inherit Visitor.frama_c_inplace
method! vterm_lval tlval =
s := Option.map (TLvalSet.add tlval) !s ; Cil.DoChildren
method! vpredicate_node = function
| Papp (li, _, _) -> begin match li.l_body with
| LBnone -> ()
| LBinductive _
| LBreads _ -> s := None
| LBpred p -> ignore (Visitor.visitFramacPredicate
(self :> Visitor.frama_c_visitor) p)
| LBterm t -> ignore (Visitor.visitFramacTerm
(self :> Visitor.frama_c_visitor) t)
end ; Cil.DoChildren
| _ -> Cil.DoChildren
end in
ignore (Visitor.visitFramacPredicate vis pred) ; !s
class strong_inv_visitor flags all_mp table = object (self)
inherit weak_inv_visitor flags all_mp table (true, `Strict)
val mutable lenient = false
method possible_interference lval ump =
let tlval = Logic_utils.lval_to_term_lval lval in
let kf = Option.get (self # current_kf) in
let pred = ump.ump_property kf [] in
let pvars = lvals_of_predicate pred in
match pvars with
| Some set -> not (
TLvalSet.fold (fun pvar ->
(&&) (Meta_simplify.neq_lval pvar tlval)
) set true
)
| None -> true
method check_annots stmt =
let aux _ a = match a.annot_content with
| AExtended (_, _, e) -> begin match e with
| {ext_name = "imeta";
ext_kind = Ext_terms [{term_node = TConst (LStr "lenient")}]} ->
lenient <- true
| _ -> () end
| _ -> ()
in
Annotations.iter_code_annot aux stmt
method! vstmt_aux stmt =
self#check_annots stmt ;
let non_loop modif ump =
let can_interfere =
not flags.simpl || match modif with
| None -> false
| Some m -> self # possible_interference m ump
in
if lenient || can_interfere then
self#instantiate ~force_after:true stmt [] ump
in
begin match stmt.skind with
| Loop _ ->
let kf = Option.get (self # current_kf) in
List.iter (fun ump ->
let prop = ump.ump_property kf ~stmt [] in
let kind = if ump.ump_assert then Assert else Check in
let annot =
Logic_const.(
new_code_annotation
(AInvariant ([], true, toplevel_predicate ~kind prop)))
in
Annotations.add_code_annot ump.ump_emitter ~kf stmt annot
) mp_todo
| Instr Set (lval, _, _) -> List.iter (non_loop (Some lval)) mp_todo
| Instr Call _ -> List.iter (self#instantiate ~force_after:true stmt []) mp_todo
| _ -> List.iter (non_loop None) mp_todo
end ;
if lenient then (
lenient <- false ;
Cil.SkipChildren
) else Cil.DoChildren
method! vfunc f =
self # fill_todo f.svar.vname;
List.iter (self # add_to_contract) mp_todo;
if mp_todo = [] then Cil.SkipChildren
else Cil.DoChildren
end
let annotate flags all_mp by_context =
let get_vis table = function
| Weak_invariant ->
(new weak_inv_visitor flags all_mp table (true, `Strict) :> Visitor.frama_c_visitor)
| Calling ->
(new calling_visitor flags all_mp table :> Visitor.frama_c_visitor)
| Writing ->
(new writing_visitor flags all_mp table :> Visitor.frama_c_visitor)
| Reading ->
(new reading_visitor flags all_mp table :> Visitor.frama_c_visitor)
| Strong_invariant ->
(new strong_inv_visitor flags all_mp table :> Visitor.frama_c_visitor)
| Precond ->
(new weak_inv_visitor flags all_mp table (true, `None) :> Visitor.frama_c_visitor)
| Postcond ->
(new weak_inv_visitor flags all_mp table (false, `Strict) :> Visitor.frama_c_visitor)
| Conditional_invariant ->
(new weak_inv_visitor flags all_mp table (false, `Conditional) :> Visitor.frama_c_visitor)
in
List.iter (fun (ctx, table) ->
let vis = get_vis table ctx in
Visitor.visitFramacFileSameGlobals vis (Ast.get ())
) by_context;
let final_vis = object (_)
inherit Visitor.frama_c_inplace
method! vfunc f =
Hashtbl.iter (fun _ ump -> ump.ump_counter := 0) all_mp;
let todo = find_hash_list Fundec_Hashtbl.find_opt delayed_contracts f in
List.iter (fun f -> f ()) (List.rev todo);
Cil.DoChildren
method! vstmt_aux stmt =
let todo = find_hash_list Stmt_Hashtbl.find_opt delayed_instances stmt in
List.iter (fun f -> f ()) (List.rev todo);
Cil.DoChildren
end in
Visitor.visitFramacFileSameGlobals final_vis (Ast.get ());
let update_cfg f =
Cfg.clearCFGinfo ~clear_id:false f;
Cfg.cfgFun f
in
Fundec_Set.iter update_cfg !cfg_recomputation;
if Fundec_Set.is_empty !cfg_recomputation then
Ast.mark_as_grown ()
else
Ast.mark_as_changed ()