~ubuntu-branches/debian/sid/frama-c/sid

« back to all changes in this revision

Viewing changes to src/kernel/cilE.ml

  • Committer: Bazaar Package Importer
  • Author(s): Mehdi Dogguy
  • Date: 2009-06-03 08:19:25 UTC
  • Revision ID: james.westby@ubuntu.com-20090603081925-kihvxvt0wy3zc4ar
Tags: upstream-20081201.dfsg
ImportĀ upstreamĀ versionĀ 20081201.dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
(**************************************************************************)
 
2
(*                                                                        *)
 
3
(*  This file is part of Frama-C.                                         *)
 
4
(*                                                                        *)
 
5
(*  Copyright (C) 2007-2008                                               *)
 
6
(*    CEA (Commissariat ļæ½ l'ļæ½nergie Atomique)                             *)
 
7
(*                                                                        *)
 
8
(*  you can redistribute it and/or modify it under the terms of the GNU   *)
 
9
(*  Lesser General Public License as published by the Free Software       *)
 
10
(*  Foundation, version 2.1.                                              *)
 
11
(*                                                                        *)
 
12
(*  It is distributed in the hope that it will be useful,                 *)
 
13
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
 
14
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
 
15
(*  GNU Lesser General Public License for more details.                   *)
 
16
(*                                                                        *)
 
17
(*  See the GNU Lesser General Public License version 2.1                 *)
 
18
(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
 
19
(*                                                                        *)
 
20
(**************************************************************************)
 
21
 
 
22
(* $Id: cilE.ml,v 1.65 2008/10/20 15:25:30 uid525 Exp $ *)
 
23
 
 
24
(** Cil extensions for Frama-C *)
 
25
 
 
26
open Cil_types
 
27
open Cilutil
 
28
open Cil
 
29
open Db_types
 
30
 
 
31
let debug = false
 
32
 
 
33
let (warn_h:(string*Cil_types.location,unit) Hashtbl.t) = Hashtbl.create 1789
 
34
 
 
35
(*
 
36
let warn_once : ('a,Format.formatter, unit, unit) format4 -> 'a =
 
37
  function fmt ->
 
38
    let v = string_of_format fmt,!Cil.currentLoc in
 
39
    try
 
40
      Hashtbl.find warn_h v;
 
41
      Printf.kapr (fun _ _ -> Obj.magic ()) (fst v)
 
42
    with Not_found ->
 
43
      Hashtbl.add warn_h v ();
 
44
      Cil.warn fmt
 
45
*)
 
46
 
 
47
let add_predicate p =
 
48
  if not (Logic_env.LogicInfo.mem p.l_name) then
 
49
    Logic_env.add_builtin_logic_function p
 
50
 
 
51
let init_builtins () =
 
52
  add_predicate  {
 
53
    l_name = "pointer_comparable";
 
54
    l_tparams = [];
 
55
    l_type = None;
 
56
    l_profile = []; (* TODO: build this one somewhere else*)
 
57
    l_labels = [];
 
58
    l_body = LBreads []; (* TODO: give an explicit definition *)
 
59
  };
 
60
  add_predicate {
 
61
    l_name = "result_finite_float";
 
62
    l_tparams = [];
 
63
    l_type = None;
 
64
    l_profile = []; (* TODO: build this one somewhere else*)
 
65
    l_labels = [];
 
66
    l_body = LBreads []; (* TODO: give an explicit definition *)
 
67
  }
 
68
 
 
69
let () = Logic_env.Builtins.extend init_builtins
 
70
 
 
71
let warn_once fmt =
 
72
    let b = Buffer.create 80 in
 
73
    let bfmt = Format.formatter_of_buffer b in
 
74
    Format.kfprintf
 
75
      (function fmt ->
 
76
        Format.pp_print_flush fmt ();
 
77
        let fmt = Buffer.contents b in
 
78
        if not (Hashtbl.mem warn_h (fmt,Cil.CurrentLoc.get ())) then begin
 
79
          Hashtbl.add warn_h (fmt,Cil.CurrentLoc.get ()) ();
 
80
          Cil.warn "%s" fmt
 
81
        end)
 
82
      bfmt fmt
 
83
 
 
84
let (log_h:(string,unit) Hashtbl.t) = Hashtbl.create 1789
 
85
 
 
86
let log_once fmt =
 
87
    let b = Buffer.create 80 in
 
88
    let bfmt = Format.formatter_of_buffer b in
 
89
    Format.kfprintf
 
90
      (function fmt ->
 
91
        Format.pp_print_flush fmt ();
 
92
        let fmt = Buffer.contents b in
 
93
        if not (Hashtbl.mem log_h fmt)
 
94
        then begin
 
95
            Hashtbl.add log_h (fmt) ();
 
96
            Cil.log "%s" fmt
 
97
          end)
 
98
      bfmt fmt
 
99
 
 
100
 
 
101
let labels_table l =
 
102
  let lbl_tbl = Hashtbl.create 17 in
 
103
  let goto_changer = object
 
104
  inherit nopCilVisitor
 
105
  method vstmt s =
 
106
    List.iter (function
 
107
                 | Label (l,_,_) -> Hashtbl.add lbl_tbl l s
 
108
                 |  _ -> ())
 
109
      s.labels;
 
110
    DoChildren
 
111
  end
 
112
  in
 
113
  List.iter (fun x -> ignore (visitCilStmt goto_changer x)) l;
 
114
  lbl_tbl
 
115
 
 
116
let update_gotos sid_tbl block =
 
117
  let goto_changer = object
 
118
  inherit nopCilVisitor
 
119
  method vstmt s = match s.skind with
 
120
  | Goto(sref,loc) ->
 
121
      (try
 
122
(*      Format.printf "Found goto %d@\n" !sref.sid;*)
 
123
      let new_stmt = Cilutil.StmtMap.find !sref sid_tbl in
 
124
(*      Format.printf "Resolved goto %d@\n" !sref.sid;*)
 
125
      ChangeTo (mkStmt (Goto (ref new_stmt,loc)))
 
126
      with Not_found -> DoChildren)
 
127
  | _ -> DoChildren
 
128
  end
 
129
  in
 
130
  visitCilBlock goto_changer block
 
131
 
 
132
let compact_body fundec =
 
133
  let labels_moved = ref false in
 
134
  let stmt_map = ref Cilutil.StmtMap.empty in
 
135
  let add_labels s l old_stmt =
 
136
    stmt_map := Cilutil.StmtMap.add old_stmt s !stmt_map ;
 
137
    s.labels <- l @ s.labels;
 
138
    labels_moved := true
 
139
  in
 
140
  let docompact(s1,s2) = match s1.skind, s2.skind with
 
141
      (* remove skip *)
 
142
    | Instr (Skip _), _ when s1.labels = [] ->
 
143
        if debug then prerr_endline "[compact_body] skip/_";
 
144
        Some [s2]
 
145
    | _, Instr (Skip _) when s2.labels = [] ->
 
146
        if debug then prerr_endline "[compact_body] _/skip";
 
147
        Some [s1]
 
148
      (* collapse useless blocks *)
 
149
    | Block b1, Block b2 when b1.battrs = b2.battrs && (s2.labels = [] || b2.bstmts <> []) ->
 
150
        if debug then prerr_endline "[compact_body] blck/blck";
 
151
        if s2.labels <> [] then begin
 
152
          let fst_b2 = List.hd b2.bstmts in
 
153
          add_labels fst_b2 s2.labels s2
 
154
        end;
 
155
        b1.bstmts <- b1.bstmts@b2.bstmts;
 
156
        Some [s1]
 
157
    | Block b, _ when b.battrs = [] ->
 
158
        if debug then prerr_endline "[compact_body] blck/instr";
 
159
        b.bstmts <- b.bstmts@[s2];
 
160
        Some [s1]
 
161
    | _, Block b when b.battrs = [] && (s2.labels = [] || b.bstmts <> []) ->
 
162
        if debug then prerr_endline "[compact_body] instr/blck";
 
163
        if s2.labels <> [] then begin
 
164
          let fst_b = List.hd b.bstmts in
 
165
          add_labels fst_b s2.labels s2
 
166
        end;
 
167
        b.bstmts <- s1::b.bstmts;
 
168
        Some [s2]
 
169
    | _ -> None
 
170
  in
 
171
  let block = fundec.sbody in
 
172
    (* Je pense que peepHole2 ne fonctionne pas correctement.
 
173
    * Peut-ļæ½tre serait-il mieux de faire notre propre fonction...
 
174
    * en plus, ļæ½a permettrait de combiner peepHole1 + peepHole2.
 
175
    * Exemple : peepHole2 ( { { x = 1; skip; } return x; } )
 
176
    *         -> { { x = 1; skip; return x; } }
 
177
    * Anne. 12/07/2007. *)
 
178
  block.bstmts <- peepHole2 docompact block.bstmts;
 
179
  let block =
 
180
    if !labels_moved then update_gotos !stmt_map block
 
181
    else block
 
182
  in let rec get_stmts b = match b.bstmts with
 
183
    | ({skind=Block blk}) :: [] ->
 
184
        if debug then prerr_endline "[compact_body] block(block)";
 
185
        get_stmts blk
 
186
    | _ -> b.bstmts
 
187
  in fundec.sbody.bstmts <- get_stmts block
 
188
 
 
189
 
 
190
let current_stmt_tbl =
 
191
  let s = Stack.create () in
 
192
  Stack.push Kglobal s;
 
193
  s
 
194
 
 
195
let start_stmt ki =
 
196
  Stack.push ki current_stmt_tbl
 
197
 
 
198
let end_stmt () =
 
199
  try
 
200
    ignore (Stack.pop current_stmt_tbl)
 
201
  with Stack.Empty -> assert false
 
202
 
 
203
let current_stmt () =
 
204
  try
 
205
    Stack.top current_stmt_tbl
 
206
  with Stack.Empty -> assert false
 
207
 
 
208
 
 
209
type syntactic_context =
 
210
  | SyNone
 
211
  | SyBinOp of Cil_types.binop * Cil_types.exp * Cil_types.exp
 
212
  | SyUnOp of  Cil_types.exp
 
213
  | SyMem of  Cil_types.lval
 
214
  | SySep of Cil_types.exp * Cil_types.exp
 
215
 
 
216
let syntactic_context = ref SyNone
 
217
let set_syntactic_context e =
 
218
 (* (match e with
 
219
  | SyBinOp (e1,e2) ->
 
220
      ignore
 
221
        (Cil.warn
 
222
           "New binary context: %a %a\n"
 
223
           Cil.d_exp e1
 
224
           Cil.d_exp e2)
 
225
  | SyUnOp e ->
 
226
      ignore
 
227
        (Cil.warn
 
228
           "New unary context: %a\n"
 
229
           Cil.d_exp e)
 
230
  | SyMem e ->
 
231
      ignore
 
232
        (Cil.warn
 
233
           "New mem context: %a\n"
 
234
           Cil.d_lval e)
 
235
  | SyNone ->       ignore
 
236
        (Cil.warn
 
237
           "New null context\n"));*)
 
238
  syntactic_context := e
 
239
 
 
240
let get_syntactic_context () = current_stmt (),!syntactic_context
 
241
 
 
242
let value_analysis_alarm_status () =
 
243
  {status = Checked{emitter="value analysis"; valid = Maybe}}
 
244
 
 
245
type alarm_behavior = Aignore | Alog | Acall of (unit -> unit)
 
246
type warn_mode = {unspecified:alarm_behavior; others: alarm_behavior;
 
247
                  imprecision_tracing:alarm_behavior}
 
248
 
 
249
let warn_all_mode =
 
250
  {unspecified=Alog; others=Alog; imprecision_tracing=Alog}
 
251
let warn_none_mode =
 
252
  {unspecified=Aignore; others=Aignore; imprecision_tracing=Aignore}
 
253
 
 
254
 
 
255
let warn_div warn_mode =
 
256
  match warn_mode.others with
 
257
    Aignore -> ()
 
258
  | Acall f -> f()
 
259
  | Alog ->
 
260
      begin
 
261
        match get_syntactic_context () with
 
262
        | _,SyNone -> ()
 
263
        | _,(SyUnOp _ | SyMem _ | SySep _) -> assert false
 
264
        | ki,SyBinOp ((Div|Mod),_,exp_d) ->
 
265
            let lexpr = Logic_const.expr_to_term exp_d in
 
266
            let annotation =
 
267
              Logic_const.new_code_annotation
 
268
                (AAssert ([],
 
269
                          Logic_const.unamed (Prel (Rneq,lexpr, lzero())),
 
270
                          value_analysis_alarm_status ()))
 
271
            in
 
272
            if Alarms.register ki Alarms.Division_alarm annotation then
 
273
              Cil.warn
 
274
                "division by zero: %a" !Ast_printer.d_code_annotation annotation
 
275
        |_,SyBinOp (_,_,_) -> assert false
 
276
      end
 
277
 
 
278
let warn_shift warn_mode size =
 
279
  match warn_mode.others with
 
280
    Aignore -> ()
 
281
  | Acall f -> f()
 
282
  | Alog ->
 
283
      begin
 
284
    match get_syntactic_context () with
 
285
    | _,SyNone -> ()
 
286
    | _,(SyUnOp _ | SyMem _ | SySep _)-> assert false
 
287
    | ki,SyBinOp ((Shiftrt | Shiftlt),_,exp_d) ->
 
288
        let lexpr = Logic_const.expr_to_term exp_d in
 
289
        let annotation =
 
290
          Logic_const.new_code_annotation
 
291
            (AAssert
 
292
                ([],
 
293
                 Logic_const.pand
 
294
                    (Logic_const.unamed (Prel (Rge,lexpr, lzero())),
 
295
                    Logic_const.unamed
 
296
                      (Prel (Rlt,lexpr, lconstant (Int64.of_int size)))),
 
297
                 value_analysis_alarm_status ()))
 
298
        in
 
299
        if Alarms.register ki Alarms.Shift_alarm annotation then
 
300
          Cil.warn "invalid shift: %a" !Ast_printer.d_code_annotation annotation;
 
301
        ()
 
302
    | _,SyBinOp(_,_,_) ->
 
303
        assert false
 
304
      end
 
305
 
 
306
let warn_mem warn_mode msg =
 
307
  match warn_mode.others with
 
308
    Aignore -> ()
 
309
  | Acall f -> f()
 
310
  | Alog ->
 
311
      begin
 
312
 
 
313
    match get_syntactic_context () with
 
314
    | _,SyNone -> ()
 
315
    | _,(SyBinOp _ | SyUnOp _ | SySep _) -> assert false
 
316
    | ki,SyMem lv_d ->
 
317
        let term = mkAddrOrStartOf lv_d in
 
318
        let lexpr = Logic_const.expr_to_tsets_elem term in
 
319
        let annotation =
 
320
          Logic_const.new_code_annotation
 
321
            (AAssert ([],
 
322
                      Logic_const.unamed (Pvalid (TSSingleton lexpr)),
 
323
                      value_analysis_alarm_status ()))
 
324
        in
 
325
        if Alarms.register ki Alarms.Memory_alarm annotation then
 
326
          Cil.warn
 
327
            "out of bounds %s. @[%a@]" msg !Ast_printer.d_code_annotation annotation;
 
328
        ( match lv_d with
 
329
          | Mem _,_ | _, (Index _ | Field _) -> ()
 
330
          | _ -> assert false)
 
331
      end
 
332
 
 
333
let warn_index warn_mode msg =
 
334
  match warn_mode.others with
 
335
    Aignore -> ()
 
336
  | Acall f -> f()
 
337
  | Alog ->
 
338
      begin
 
339
    match get_syntactic_context () with
 
340
    | _,SyNone -> ()
 
341
    | _,(SyMem _ | SyUnOp _ | SySep _) -> assert false
 
342
    | ki ,SyBinOp (IndexPI,e1,e2) ->
 
343
        let lexpr = Logic_const.expr_to_term e1 in
 
344
        let rexpr = Logic_const.expr_to_term e2 in
 
345
        let p0 = Logic_const.prel(Rle, lzero(), lexpr) in
 
346
        let p1 = Logic_const.prel(Rlt, lexpr, rexpr) in
 
347
        let p = Logic_const.pand (p0,p1) in
 
348
        let annotation =
 
349
          Logic_const.new_code_annotation (AAssert ([],p,value_analysis_alarm_status ()))
 
350
        in
 
351
        if Alarms.register ki Alarms.Memory_alarm annotation then
 
352
          Cil.warn
 
353
            "%s out of bounds index. @[%a@]"
 
354
            msg !Ast_printer.d_code_annotation annotation
 
355
    | _,SyBinOp(_,_,_) ->
 
356
        assert false
 
357
      end
 
358
 
 
359
let warn_mem_read warn_mode = warn_mem warn_mode "read"
 
360
let warn_mem_write warn_mode = warn_mem warn_mode "write"
 
361
 
 
362
let comparable_pointers t1 t2 =
 
363
  let pi = Logic_env.find_logic_function "pointer_comparable" in
 
364
  Papp (pi, [], [t1;t2])
 
365
 
 
366
let warn_pointer_comparison warn_mode =
 
367
  match warn_mode.others with
 
368
    Aignore -> ()
 
369
  | Acall f -> f()
 
370
  | Alog ->
 
371
      begin
 
372
  match get_syntactic_context () with
 
373
  | _,SyNone -> ()
 
374
  | _,(SyUnOp _ | SyMem _ | SySep _) -> assert false
 
375
  | ki,SyBinOp ((Eq|Ne|Ge|Le|Gt|Lt),exp_l,exp_r) ->
 
376
      let lexpr_l = Logic_const.expr_to_term exp_l in
 
377
      let lexpr_r = Logic_const.expr_to_term exp_r in
 
378
      let annotation =
 
379
        Logic_const.new_code_annotation
 
380
          (AAssert ([],
 
381
                    Logic_const.unamed (comparable_pointers lexpr_l lexpr_r),
 
382
                    value_analysis_alarm_status ())) in
 
383
      if Alarms.register ki Alarms.Pointer_compare_alarm annotation then
 
384
        warn "pointer comparison: %a" !Ast_printer.d_code_annotation annotation
 
385
  | _,SyBinOp(_,_,_) ->
 
386
      assert false
 
387
end
 
388
 
 
389
let result_nan_infinite _op t1 t2 =
 
390
  let pi = Logic_env.find_logic_function "result_finite_float" in
 
391
  Papp (pi, [], [t1;t2])
 
392
 
 
393
let warn_result_nan_infinite warn_mode =
 
394
  match warn_mode.others with
 
395
    Aignore -> ()
 
396
  | Acall f -> f()
 
397
  | Alog ->
 
398
      begin
 
399
  match get_syntactic_context () with
 
400
  | _,SyNone -> ()
 
401
  | _,(SyUnOp _ | SyMem _ | SySep _) -> assert false
 
402
  | ki,SyBinOp (_op, exp_l, exp_r) ->
 
403
      let lexpr_l = Logic_const.expr_to_term exp_l in
 
404
      let lexpr_r = Logic_const.expr_to_term exp_r in
 
405
      let op = lexpr_l in (* TODO: use op *)
 
406
      let annotation =
 
407
        Logic_const.new_code_annotation
 
408
          (AAssert
 
409
             ([],
 
410
              Logic_const.unamed (result_nan_infinite op lexpr_l lexpr_r),
 
411
              value_analysis_alarm_status ())) in
 
412
      if Alarms.register ki Alarms.Result_is_nan_or_infinite_alarm annotation
 
413
      then
 
414
        warn "float operation: %a" !Ast_printer. d_code_annotation annotation
 
415
end
 
416
 
 
417
let warn_uninitialized warn_mode =
 
418
  match warn_mode.unspecified with
 
419
    Aignore -> ()
 
420
  | Acall f -> f()
 
421
  | Alog ->
 
422
      begin
 
423
    match get_syntactic_context () with
 
424
    | _,SyNone -> ()
 
425
    | _,(SyBinOp _ | SyUnOp _ | SySep _) -> assert false
 
426
    | _,SyMem lv_d ->
 
427
        warn_once "(TODO: emit a proper alarm) accessing uninitialized left-value: %a" d_lval lv_d
 
428
      end
 
429
 
 
430
let warn_escapingaddr warn_mode =
 
431
  match warn_mode.unspecified with
 
432
    Aignore -> ()
 
433
  | Acall f -> f()
 
434
  | Alog ->
 
435
      begin
 
436
    match get_syntactic_context () with
 
437
    | _,SyNone -> ()
 
438
    | _,(SyBinOp _ | SyUnOp _ | SySep _) -> assert false
 
439
    | _,SyMem lv_d ->
 
440
        warn_once "(TODO: emit a proper alarm) accessing left-value that contains escaping addresses : %a" d_lval lv_d
 
441
      end
 
442
 
 
443
let warn_separated warn_mode =
 
444
  match warn_mode.unspecified with
 
445
      Aignore -> ()
 
446
    | Acall f -> f()
 
447
    | Alog ->
 
448
        begin
 
449
          match get_syntactic_context () with
 
450
            | _,SyNone -> ()
 
451
            | _,(SyBinOp _ | SyUnOp _ | SyMem _ ) -> assert false
 
452
            | ki,SySep(lv1,lv2) ->
 
453
                let llv1 = Logic_const.expr_to_tsets_elem lv1 in
 
454
                let llv2 = Logic_const.expr_to_tsets_elem lv2 in
 
455
                let tlv1 = TSSingleton llv1 in
 
456
                let tlv2 = TSSingleton llv2 in
 
457
                let alarm =
 
458
                  Logic_const.pseparated ~loc:(Instr.loc ki) [tlv1; tlv2]
 
459
                in let annotation =
 
460
                  Logic_const.new_code_annotation
 
461
                    (AAssert([],alarm,value_analysis_alarm_status ()))
 
462
                in
 
463
                if Alarms.register ki Alarms.Separation_alarm annotation then
 
464
                warn
 
465
                  "multiple accesses in unspecified order. \
 
466
                   assert \\separated(%a,%a);" d_exp lv1 d_exp lv2
 
467
        end
 
468
(*
 
469
Local Variables:
 
470
compile-command: "LC_ALL=C make -C ../.."
 
471
End:
 
472
*)