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

« back to all changes in this revision

Viewing changes to cil/src/check.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
(*  Copyright (C) 2001-2003,                                              *)
 
4
(*   George C. Necula    <necula@cs.berkeley.edu>                         *)
 
5
(*   Scott McPeak        <smcpeak@cs.berkeley.edu>                        *)
 
6
(*   Wes Weimer          <weimer@cs.berkeley.edu>                         *)
 
7
(*   Ben Liblit          <liblit@cs.berkeley.edu>                         *)
 
8
(*  All rights reserved.                                                  *)
 
9
(*                                                                        *)
 
10
(*  Redistribution and use in source and binary forms, with or without    *)
 
11
(*  modification, are permitted provided that the following conditions    *)
 
12
(*  are met:                                                              *)
 
13
(*                                                                        *)
 
14
(*  1. Redistributions of source code must retain the above copyright     *)
 
15
(*  notice, this list of conditions and the following disclaimer.         *)
 
16
(*                                                                        *)
 
17
(*  2. Redistributions in binary form must reproduce the above copyright  *)
 
18
(*  notice, this list of conditions and the following disclaimer in the   *)
 
19
(*  documentation and/or other materials provided with the distribution.  *)
 
20
(*                                                                        *)
 
21
(*  3. The names of the contributors may not be used to endorse or        *)
 
22
(*  promote products derived from this software without specific prior    *)
 
23
(*  written permission.                                                   *)
 
24
(*                                                                        *)
 
25
(*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   *)
 
26
(*  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     *)
 
27
(*  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS     *)
 
28
(*  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE        *)
 
29
(*  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,   *)
 
30
(*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  *)
 
31
(*  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      *)
 
32
(*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER      *)
 
33
(*  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT    *)
 
34
(*  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN     *)
 
35
(*  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *)
 
36
(*  POSSIBILITY OF SUCH DAMAGE.                                           *)
 
37
(*                                                                        *)
 
38
(*  File modified by CEA (Commissariat ļæ½ l'ļæ½nergie Atomique).             *)
 
39
(**************************************************************************)
 
40
 
 
41
(* A consistency checker for CIL *)
 
42
open Cil_types
 
43
open Cil
 
44
module E = Errormsg
 
45
module H = Hashtbl
 
46
open Pretty
 
47
 
 
48
 
 
49
(* A few parameters to customize the checking *)
 
50
type checkFlags =
 
51
    NoCheckGlobalIds   (* Do not check that the global ids have the proper
 
52
                        * hash value *)
 
53
 
 
54
let checkGlobalIds = ref true
 
55
 
 
56
  (* Attributes must be sorted *)
 
57
type ctxAttr =
 
58
    CALocal                             (* Attribute of a local variable *)
 
59
  | CAGlobal                            (* Attribute of a global variable *)
 
60
  | CAType                              (* Attribute of a type *)
 
61
 
 
62
let valid = ref true
 
63
 
 
64
let warn fmt =
 
65
  valid := false;
 
66
  Cil.warn fmt
 
67
 
 
68
let warnContext fmt =
 
69
  valid := false;
 
70
  Cil.warnContext fmt
 
71
 
 
72
let checkAttributes (attrs: attribute list) : unit =
 
73
  let rec loop lastname = function
 
74
      [] -> ()
 
75
    | Attr(an, _) :: resta ->
 
76
        if an < lastname then
 
77
          ignore (warn "Attributes not sorted");
 
78
        loop an resta
 
79
  in
 
80
  loop "" attrs
 
81
 
 
82
 
 
83
  (* Keep track of defined types *)
 
84
let typeDefs : (string, typ) H.t = H.create 117
 
85
 
 
86
 
 
87
  (* Keep track of all variables names, enum tags and type names *)
 
88
let varNamesEnv : (string, unit) H.t = H.create 117
 
89
 
 
90
  (* We also keep a map of variables indexed by id, to ensure that only one
 
91
   * varinfo has a given id *)
 
92
let varIdsEnv: (int, varinfo) H.t = H.create 117
 
93
 
 
94
  (* And keep track of all varinfo's to check the uniqueness of the
 
95
   * identifiers *)
 
96
let allBases: (int, varinfo) H.t = H.create 117
 
97
 
 
98
 (* Also keep a list of environments. We place an empty string in the list to
 
99
  * mark the start of a local environment (i.e. a function) *)
 
100
let varNamesList : (string * int) list ref = ref []
 
101
let defineName s =
 
102
  if s = "" then
 
103
    E.s (bug "Empty name\n");
 
104
  if H.mem varNamesEnv s then
 
105
    ignore (warn "Multiple definitions for %s\n" s);
 
106
  H.add varNamesEnv s ()
 
107
 
 
108
let defineVariable vi =
 
109
  defineName vi.vname;
 
110
  varNamesList := (vi.vname, vi.vid) :: !varNamesList;
 
111
  (* Check the id *)
 
112
  if H.mem allBases vi.vid then
 
113
    ignore (warn "Id %d is already defined (%s)\n" vi.vid
 
114
              (!Cil.output_ident vi.vname));
 
115
  H.add allBases vi.vid vi;
 
116
  (* And register it in the current scope also *)
 
117
  H.add varIdsEnv vi.vid vi
 
118
 
 
119
(* Check that a varinfo has already been registered *)
 
120
let checkVariable vi =
 
121
  try
 
122
    (* Check in the current scope only *)
 
123
    if vi != H.find varIdsEnv vi.vid then
 
124
      ignore (warnContext "varinfos for %s not shared\n"
 
125
                (!Cil.output_ident vi.vname));
 
126
  with Not_found ->
 
127
    ignore (warn "Unknown id (%d) for %s\n" vi.vid
 
128
              (!Cil.output_ident vi.vname))
 
129
 
 
130
 
 
131
let startEnv () =
 
132
  varNamesList := ("", -1) :: !varNamesList
 
133
 
 
134
let endEnv () =
 
135
  let rec loop = function
 
136
      [] -> E.s (bug "Cannot find start of env")
 
137
    | ("", _) :: rest -> varNamesList := rest
 
138
    | (s, id) :: rest -> begin
 
139
        H.remove varNamesEnv s;
 
140
        H.remove varIdsEnv id;
 
141
        loop rest
 
142
    end
 
143
  in
 
144
  loop !varNamesList
 
145
 
 
146
 
 
147
 
 
148
(* The current function being checked *)
 
149
let currentReturnType : typ ref = ref voidType
 
150
 
 
151
(* A map of labels in the current function *)
 
152
let labels: (string, unit) H.t = H.create 17
 
153
 
 
154
(* A list of statements seen in the current function *)
 
155
let statements: stmt list ref = ref []
 
156
 
 
157
(* A list of the targets of Gotos *)
 
158
let gotoTargets: (string * stmt) list ref = ref []
 
159
 
 
160
(*** TYPES ***)
 
161
(* Cetain types can only occur in some contexts, so keep a list of context *)
 
162
type ctxType =
 
163
    CTStruct                            (* In a composite type *)
 
164
  | CTUnion
 
165
  | CTFArg                              (* In a function argument type *)
 
166
  | CTFRes                              (* In a function result type *)
 
167
  | CTArray                             (* In an array type *)
 
168
  | CTPtr                               (* In a pointer type *)
 
169
  | CTExp                               (* In an expression, as the type of
 
170
                                         * the result of binary operators, or
 
171
                                         * in a cast *)
 
172
  | CTSizeof                            (* In a sizeof *)
 
173
  | CTDecl                              (* In a typedef, or a declaration *)
 
174
 
 
175
let d_context () = function
 
176
    CTStruct -> text "CTStruct"
 
177
  | CTUnion -> text "CTUnion"
 
178
  | CTFArg -> text "CTFArg"
 
179
  | CTFRes -> text "CTFRes"
 
180
  | CTArray -> text "CTArray"
 
181
  | CTPtr -> text "CTPtr"
 
182
  | CTExp -> text "CTExp"
 
183
  | CTSizeof -> text "CTSizeof"
 
184
  | CTDecl -> text "CTDecl"
 
185
 
 
186
 
 
187
(* Keep track of all tags that we use. For each tag remember also the info
 
188
 * structure and a flag whether it was actually defined or just used. A
 
189
 * forward declaration acts as a definition. *)
 
190
type defuse =
 
191
    Defined (* We actually have seen a definition of this tag *)
 
192
  | Forward (* We have seen a forward declaration for it. This is done using
 
193
             * a GType with an empty type name *)
 
194
  | Used    (* Only uses *)
 
195
let compUsed : (int, compinfo * defuse ref) H.t = H.create 117
 
196
let enumUsed : (string, enuminfo * defuse ref) H.t = H.create 117
 
197
let typUsed  : (string, typeinfo * defuse ref) H.t = H.create 117
 
198
 
 
199
(* For composite types we also check that the names are unique *)
 
200
let compNames : (string, unit) H.t = H.create 17
 
201
 
 
202
 
 
203
  (* Check a type *)
 
204
let rec checkType (t: typ) (ctx: ctxType) =
 
205
  (* Check that it appears in the right context *)
 
206
  let rec checkContext = function
 
207
      TVoid _ -> ctx = CTPtr || ctx = CTFRes || ctx = CTDecl
 
208
    | TNamed (ti, a) -> checkContext ti.ttype
 
209
    | TArray _ ->
 
210
        (ctx = CTStruct || ctx = CTUnion
 
211
         || ctx = CTSizeof || ctx = CTDecl || ctx = CTArray || ctx = CTPtr)
 
212
    | TComp _ -> ctx <> CTExp
 
213
    | _ -> true
 
214
  in
 
215
  if not (checkContext t) then
 
216
    ignore (warn "Type (%a) used in wrong context. Expected context: %a"
 
217
              d_plaintype t d_context ctx);
 
218
  match t with
 
219
    (TVoid a | TBuiltin_va_list a) -> checkAttributes a
 
220
  | TInt (ik, a) -> checkAttributes a
 
221
  | TFloat (_, a) -> checkAttributes a
 
222
  | TPtr (t, a) -> checkAttributes a;  checkType t CTPtr
 
223
 
 
224
  | TNamed (ti, a) ->
 
225
      checkAttributes a;
 
226
      if ti.tname = "" then
 
227
        ignore (warnContext "Using a typeinfo for an empty-named type\n");
 
228
      checkTypeInfo Used ti
 
229
 
 
230
  | TComp (comp, a) ->
 
231
      checkAttributes a;
 
232
      (* Mark it as a forward. We'll check it later. If we try to check it
 
233
       * now we might encounter undefined types *)
 
234
      checkCompInfo Used comp
 
235
 
 
236
 
 
237
  | TEnum (enum, a) -> begin
 
238
      checkAttributes a;
 
239
      checkEnumInfo Used enum
 
240
  end
 
241
 
 
242
  | TArray(bt, len, a) ->
 
243
      checkAttributes a;
 
244
      checkType bt CTArray;
 
245
      (match len with
 
246
        None -> ()
 
247
      | Some l -> begin
 
248
          let t = checkExp true l in
 
249
          match t with
 
250
            TInt((IInt|IUInt), _) -> ()
 
251
          | _ -> E.s (bug "Type of array length is not integer")
 
252
      end)
 
253
 
 
254
  | TFun (rt, targs, isva, a) ->
 
255
      checkAttributes a;
 
256
      checkType rt CTFRes;
 
257
      List.iter
 
258
        (fun (an, at, aa) ->
 
259
          checkType at CTFArg;
 
260
          checkAttributes aa) (argsToList targs)
 
261
 
 
262
(* Check that a type is a promoted integral type *)
 
263
and checkIntegralType (t: typ) =
 
264
  checkType t CTExp;
 
265
  match unrollType t with
 
266
    TInt _ -> ()
 
267
  | _ -> ignore (warn "Non-integral type")
 
268
 
 
269
(* Check that a type is a promoted arithmetic type *)
 
270
and checkArithmeticType (t: typ) =
 
271
  checkType t CTExp;
 
272
  match unrollType t with
 
273
    TInt _ | TFloat _ -> ()
 
274
  | _ -> ignore (warn "Non-arithmetic type")
 
275
 
 
276
(* Check that a type is a promoted boolean type *)
 
277
and checkBooleanType (t: typ) =
 
278
  checkType t CTExp;
 
279
  match unrollType t with
 
280
    TInt _ | TFloat _ | TPtr _ -> ()
 
281
  | _ -> ignore (warn "Non-boolean type")
 
282
 
 
283
 
 
284
(* Check that a type is a pointer type *)
 
285
and checkPointerType (t: typ) =
 
286
  checkType t CTExp;
 
287
  match unrollType t with
 
288
    TPtr _ -> ()
 
289
  | _ -> ignore (warn "Non-pointer type")
 
290
 
 
291
 
 
292
and typeMatch (t1: typ) (t2: typ) =
 
293
  if typeSig t1 <> typeSig t2 then
 
294
    match unrollType t1, unrollType t2 with
 
295
    (* Allow free interchange of TInt and TEnum *)
 
296
      TInt (IInt, _), TEnum _ -> ()
 
297
    | TEnum _, TInt (IInt, _) -> ()
 
298
 
 
299
    | _, _ -> ignore (warn "Type mismatch:@\n    %a@!and %a@\n"
 
300
                        d_type t1 d_type t2)
 
301
 
 
302
and checkCompInfo (isadef: defuse) comp =
 
303
  let fullname = compFullName comp in
 
304
  try
 
305
    let oldci, olddef = H.find compUsed comp.ckey in
 
306
    (* Check that it is the same *)
 
307
    if oldci != comp then
 
308
      ignore (warnContext "compinfo for %s not shared\n" fullname);
 
309
    (match !olddef, isadef with
 
310
    | Defined, Defined ->
 
311
        ignore (warnContext "Multiple definition of %s\n" fullname)
 
312
    | _, Defined -> olddef := Defined
 
313
    | Defined, _ -> ()
 
314
    | _, Forward -> olddef := Forward
 
315
    | _, _ -> ())
 
316
  with Not_found -> begin (* This is the first time we see it *)
 
317
    (* Check that the name is not empty *)
 
318
    if comp.cname = "" then
 
319
      E.s (bug "Compinfo with empty name");
 
320
    (* Check that the name is unique *)
 
321
    if H.mem compNames fullname then
 
322
      ignore (warn "Duplicate name %s" fullname);
 
323
    (* Add it to the map before we go on *)
 
324
    H.add compUsed comp.ckey (comp, ref isadef);
 
325
    H.add compNames fullname ();
 
326
    (* Do not check the compinfo unless this is a definition. Otherwise you
 
327
     * might run into undefined types. *)
 
328
    if isadef = Defined then begin
 
329
      checkAttributes comp.cattr;
 
330
      let fctx = if comp.cstruct then CTStruct else CTUnion in
 
331
      let rec checkField f =
 
332
        if not
 
333
            (f.fcomp == comp &&  (* Each field must share the self cell of
 
334
             * the host *)
 
335
             f.fname <> "") then
 
336
          ignore (warn "Self pointer not set in field %s of %s"
 
337
                    f.fname fullname);
 
338
        checkType f.ftype fctx;
 
339
        (* Check the bitfields *)
 
340
        (match unrollType f.ftype, f.fbitfield with
 
341
        | TInt (ik, a), Some w ->
 
342
            checkAttributes a;
 
343
            if w < 0 || w >= bitsSizeOf (TInt(ik, a)) then
 
344
              ignore (warn "Wrong width (%d) in bitfield" w)
 
345
        | _, Some w ->
 
346
            ignore (E.error "Bitfield on a non integer type\n")
 
347
        | _ -> ());
 
348
        checkAttributes f.fattr
 
349
      in
 
350
      List.iter checkField comp.cfields
 
351
    end
 
352
  end
 
353
 
 
354
 
 
355
and checkEnumInfo (isadef: defuse) enum =
 
356
  if enum.ename = "" then
 
357
    E.s (bug "Enuminfo with empty name");
 
358
  try
 
359
    let oldei, olddef = H.find enumUsed enum.ename in
 
360
    (* Check that it is the same *)
 
361
    if oldei != enum then
 
362
      ignore (warnContext "enuminfo for %s not shared\n" enum.ename);
 
363
    (match !olddef, isadef with
 
364
      Defined, Defined ->
 
365
        ignore (warnContext "Multiple definition of enum %s\n" enum.ename)
 
366
    | _, Defined -> olddef := Defined
 
367
    | Defined, _ -> ()
 
368
    | _, Forward -> olddef := Forward
 
369
    | _, _ -> ())
 
370
  with Not_found -> begin (* This is the first time we see it *)
 
371
    (* Add it to the map before we go on *)
 
372
    H.add enumUsed enum.ename (enum, ref isadef);
 
373
    checkAttributes enum.eattr;
 
374
    List.iter (fun (tn, _, _) -> defineName tn) enum.eitems;
 
375
  end
 
376
 
 
377
and checkTypeInfo (isadef: defuse) ti =
 
378
  try
 
379
    let oldti, olddef = H.find typUsed ti.tname in
 
380
    (* Check that it is the same *)
 
381
    if oldti != ti then
 
382
      ignore (warnContext "typeinfo for %s not shared\n" ti.tname);
 
383
    (match !olddef, isadef with
 
384
      Defined, Defined ->
 
385
        ignore (warnContext "Multiple definition of type %s\n" ti.tname)
 
386
    | Defined, Used -> ()
 
387
    | Used, Defined ->
 
388
        ignore (warnContext "Use of type %s before its definition\n" ti.tname)
 
389
    | _, _ ->
 
390
        ignore (warnContext "Bug in checkTypeInfo for %s\n" ti.tname))
 
391
  with Not_found -> begin (* This is the first time we see it *)
 
392
    if ti.tname = "" then
 
393
      ignore (warnContext "typeinfo with empty name");
 
394
    checkType ti.ttype CTDecl;
 
395
    (* Add it to the map before we go on *)
 
396
    H.add typUsed ti.tname (ti, ref isadef);
 
397
  end
 
398
 
 
399
(* Check an lvalue. If isconst then the lvalue appears in a context where
 
400
 * only a compile-time constant can appear. Return the type of the lvalue.
 
401
 * See the typing rule from cil.mli *)
 
402
and checkLval (isconst: bool) (lv: lval) : typ =
 
403
  match lv with
 
404
    Var vi, off ->
 
405
      checkVariable vi;
 
406
      checkOffset vi.vtype off
 
407
 
 
408
  | Mem addr, off -> begin
 
409
      if isconst then
 
410
        ignore (warn "Memory operation in constant");
 
411
      let ta = checkExp false addr in
 
412
      match unrollType ta with
 
413
        TPtr (t, _) -> checkOffset t off
 
414
      | _ -> E.s (bug "Mem on a non-pointer")
 
415
  end
 
416
 
 
417
(* Check an offset. The basetype is the type of the object referenced by the
 
418
 * base. Return the type of the lvalue constructed from a base value of right
 
419
 * type and the offset. See the typing rules from cil.mli *)
 
420
and checkOffset basetyp : offset -> typ = function
 
421
    NoOffset -> basetyp
 
422
  | Index (ei, o) ->
 
423
      checkExpType false ei intType;
 
424
      begin
 
425
        match unrollType basetyp with
 
426
          TArray (t, _, _) -> checkOffset t o
 
427
        | t -> E.s (bug "typeOffset: Index on a non-array: %a" d_plaintype t)
 
428
      end
 
429
 
 
430
  | Field (fi, o) ->
 
431
      (* Now check that the host is shared propertly *)
 
432
      checkCompInfo Used fi.fcomp;
 
433
      (* Check that this exact field is part of the host *)
 
434
      if not (List.exists (fun f -> f == fi) fi.fcomp.cfields) then
 
435
        ignore (warn "Field %s not part of %s"
 
436
                  fi.fname (compFullName fi.fcomp));
 
437
      checkOffset fi.ftype o
 
438
 
 
439
and checkExpType (isconst: bool) (e: exp) (t: typ) =
 
440
  let t' = checkExp isconst e in (* compute the type *)
 
441
  if isconst then begin (* For initializers allow a string to initialize an
 
442
                         * array of characters  *)
 
443
    if typeSig t' <> typeSig t then
 
444
      match e, t with
 
445
      | _ -> typeMatch t' t
 
446
  end else
 
447
    typeMatch t' t
 
448
 
 
449
(* Check an expression. isconst specifies if the expression occurs in a
 
450
 * context where only a compile-time constant can occur. Return the computed
 
451
 * type of the expression *)
 
452
and checkExp (isconst: bool) (e: exp) : typ =
 
453
  E.withContext
 
454
    (fun _ -> dprintf "check%s: %a"
 
455
        (if isconst then "Const" else "Exp") d_exp e)
 
456
    (fun _ ->
 
457
      match e with
 
458
      | Const(CInt64 (_, ik, _)) -> TInt(ik, [])
 
459
      | Const(CChr _) -> charType
 
460
      | Const(CStr s) -> charPtrType
 
461
      | Const(CWStr s) -> TPtr(!wcharType,[])
 
462
      | Const(CReal (_, fk, _)) -> TFloat(fk, [])
 
463
      | Const(CEnum (_, _, ei)) -> TEnum(ei, [])
 
464
      | Lval(lv) ->
 
465
          if isconst then
 
466
            ignore (warn "Lval in constant");
 
467
          checkLval isconst lv
 
468
 
 
469
      | SizeOf(t) -> begin
 
470
          (* Sizeof cannot be applied to certain types *)
 
471
          checkType t CTSizeof;
 
472
          (match unrollType t with
 
473
            (TFun _ | TVoid _) ->
 
474
              ignore (warn "Invalid operand for sizeof")
 
475
          | _ ->());
 
476
          uintType
 
477
      end
 
478
      | SizeOfE(e) ->
 
479
          (* The expression in a sizeof can be anything *)
 
480
          let te = checkExp false e in
 
481
          checkExp isconst (SizeOf(te))
 
482
 
 
483
      | SizeOfStr s -> uintType
 
484
 
 
485
      | AlignOf(t) -> begin
 
486
          (* Sizeof cannot be applied to certain types *)
 
487
          checkType t CTSizeof;
 
488
          (match unrollType t with
 
489
            (TFun _ | TVoid _) ->
 
490
              ignore (warn "Invalid operand for sizeof")
 
491
          | _ ->());
 
492
          uintType
 
493
      end
 
494
      | AlignOfE(e) ->
 
495
          (* The expression in an AlignOfE can be anything *)
 
496
          let te = checkExp false e in
 
497
          checkExp isconst (AlignOf(te))
 
498
 
 
499
      | UnOp (Neg, e, tres) ->
 
500
          checkArithmeticType tres; checkExpType isconst e tres; tres
 
501
 
 
502
      | UnOp (BNot, e, tres) ->
 
503
          checkIntegralType tres; checkExpType isconst e tres; tres
 
504
 
 
505
      | UnOp (LNot, e, tres) ->
 
506
          let te = checkExp isconst e in
 
507
          checkBooleanType te;
 
508
          checkIntegralType tres; (* Must check that t is well-formed *)
 
509
          typeMatch tres intType;
 
510
          tres
 
511
 
 
512
      | BinOp (bop, e1, e2, tres) -> begin
 
513
          let t1 = checkExp isconst e1 in
 
514
          let t2 = checkExp isconst e2 in
 
515
          match bop with
 
516
            (Mult | Div) ->
 
517
              typeMatch t1 t2; checkArithmeticType tres;
 
518
              typeMatch t1 tres; tres
 
519
          | (Eq|Ne|Lt|Le|Ge|Gt) ->
 
520
              typeMatch t1 t2; checkArithmeticType t1;
 
521
              typeMatch tres intType; tres
 
522
          | Mod|BAnd|BOr|BXor ->
 
523
              typeMatch t1 t2; checkIntegralType tres;
 
524
              typeMatch t1 tres; tres
 
525
          | LAnd | LOr ->
 
526
              typeMatch t1 t2; checkBooleanType tres;
 
527
              typeMatch t1 tres; tres
 
528
          | Shiftlt | Shiftrt ->
 
529
              typeMatch t1 tres; checkIntegralType t1;
 
530
              checkIntegralType t2; tres
 
531
          | (PlusA | MinusA) ->
 
532
                typeMatch t1 t2; typeMatch t1 tres;
 
533
                checkArithmeticType tres; tres
 
534
          | (PlusPI | MinusPI | IndexPI) ->
 
535
              checkPointerType tres;
 
536
              typeMatch t1 tres;
 
537
              checkIntegralType t2;
 
538
              tres
 
539
          | MinusPP  ->
 
540
              checkPointerType t1; checkPointerType t2;
 
541
              typeMatch t1 t2;
 
542
              typeMatch tres intType;
 
543
              tres
 
544
      end
 
545
      | AddrOf (lv) -> begin
 
546
          let tlv = checkLval isconst lv in
 
547
          (* Only certain types can be in AddrOf *)
 
548
          match unrollType tlv with
 
549
          | TVoid _ ->
 
550
              E.s (bug "AddrOf on improper type");
 
551
 
 
552
          | (TInt _ | TFloat _ | TPtr _ | TComp _ | TFun _ | TArray _ ) ->
 
553
              TPtr(tlv, [])
 
554
 
 
555
          | TEnum _ -> intPtrType
 
556
          | _ -> E.s (bug "AddrOf on unknown type")
 
557
      end
 
558
 
 
559
      | StartOf lv -> begin
 
560
          let tlv = checkLval isconst lv in
 
561
          match unrollType tlv with
 
562
            TArray (t,_, _) -> TPtr(t, [])
 
563
          | _ -> E.s (bug "StartOf on a non-array")
 
564
      end
 
565
 
 
566
      | CastE (tres, e) -> begin
 
567
          let et = checkExp isconst e in
 
568
          checkType tres CTExp;
 
569
          (* Not all types can be cast *)
 
570
          match unrollType et with
 
571
            TArray _ -> E.s (bug "Cast of an array type")
 
572
          | TFun _ -> E.s (bug "Cast of a function type")
 
573
          | TComp _ -> E.s (bug "Cast of a composite type")
 
574
          | TVoid _ -> E.s (bug "Cast of a void type")
 
575
          | _ -> tres
 
576
      end)
 
577
    () (* The argument of withContext *)
 
578
 
 
579
and checkInit  (i: init) : typ =
 
580
  E.withContext
 
581
    (fun _ -> dprintf "checkInit: %a" d_init i)
 
582
    (fun _ ->
 
583
      match i with
 
584
        SingleInit e -> checkExp true e
 
585
(*
 
586
      | ArrayInit (bt, len, initl) -> begin
 
587
          checkType bt CTSizeof;
 
588
          if List.length initl > len then
 
589
            ignore (warn "Too many initializers in array");
 
590
          List.iter (fun i -> checkInitType i bt) initl;
 
591
          TArray(bt, Some (integer len), [])
 
592
      end
 
593
*)
 
594
      | CompoundInit (ct, initl) -> begin
 
595
          checkType ct CTSizeof;
 
596
          (match unrollType ct with
 
597
            TArray(bt, Some (Const(CInt64(len, _, _))), _) ->
 
598
              let rec loopIndex i = function
 
599
                  [] ->
 
600
                    if i <> len then
 
601
                      ignore (warn "Wrong number of initializers in array")
 
602
 
 
603
                | (Index(Const(CInt64(i', _, _)), NoOffset), ei) :: rest ->
 
604
                    if i' <> i then
 
605
                      ignore (warn "Initializer for index %s when %s was expected\n"
 
606
                                (Int64.format "%d" i') (Int64.format "%d" i));
 
607
                    checkInitType ei bt;
 
608
                    loopIndex (Int64.succ i) rest
 
609
                | _ :: rest ->
 
610
                    ignore (warn "Malformed initializer for array element")
 
611
              in
 
612
              loopIndex Int64.zero initl
 
613
          | TArray(_, _, _) ->
 
614
              ignore (warn "Malformed initializer for array")
 
615
          | TComp (comp, _) ->
 
616
              if comp.cstruct then
 
617
                let rec loopFields
 
618
                    (nextflds: fieldinfo list)
 
619
                    (initl: (offset * init) list) : unit =
 
620
                  match nextflds, initl with
 
621
                    [], [] -> ()   (* We are done *)
 
622
                  | f :: restf, (Field(f', NoOffset), i) :: resti ->
 
623
                      if f.fname <> f'.fname then
 
624
                        ignore (warn "Expected initializer for field %s and found one for %s\n" f.fname f'.fname);
 
625
                      checkInitType i f.ftype;
 
626
                      loopFields restf resti
 
627
                  | [], _ :: _ ->
 
628
                      ignore (warn "Too many initializers for struct")
 
629
                  | _ :: _, [] ->
 
630
                      ignore (warn "Too few initializers for struct")
 
631
                  | _, _ ->
 
632
                      ignore (warn "Malformed initializer for struct")
 
633
                in
 
634
                loopFields
 
635
                  (List.filter (fun f -> f.fname <> missingFieldName)
 
636
                     comp.cfields)
 
637
                  initl
 
638
 
 
639
              else (* UNION *)
 
640
                if comp.cfields == [] then begin
 
641
                  if initl != [] then
 
642
                    ignore (warn "Initializer for empty union not empty");
 
643
                end else begin
 
644
                  match initl with
 
645
                    [(Field(f, NoOffset), ei)] ->
 
646
                      if f.fcomp != comp then
 
647
                        ignore (bug "Wrong designator for union initializer");
 
648
                      if !msvcMode && f != List.hd comp.cfields then
 
649
                        ignore (warn "On MSVC you can only initialize the first field of a union");
 
650
                      checkInitType ei f.ftype
 
651
 
 
652
                  | _ ->
 
653
                      ignore (warn "Malformed initializer for union")
 
654
                end
 
655
          | _ ->
 
656
              E.s (warn "Type of Compound is not array or struct or union"));
 
657
          ct
 
658
      end)
 
659
    () (* The arguments of withContext *)
 
660
 
 
661
 
 
662
and checkInitType (i: init) (t: typ) : unit =
 
663
  let it = checkInit i in
 
664
  typeMatch it t
 
665
 
 
666
and checkStmt (s: stmt) =
 
667
  E.withContext
 
668
    (fun _ ->
 
669
      (* Print context only for certain small statements *)
 
670
      match s.skind with
 
671
        Loop _ | If _ | Switch _  -> nil
 
672
      | _ -> dprintf "checkStmt: %a" d_stmt s)
 
673
    (fun _ ->
 
674
      (* Check the labels *)
 
675
      let checkLabel = function
 
676
          Label (ln, l, _) ->
 
677
            if H.mem labels ln then
 
678
              ignore (warn "Multiply defined label %s" ln);
 
679
            H.add labels ln ()
 
680
        | Case (e, _) -> checkExpType true e intType
 
681
        | _ -> () (* Not yet implemented *)
 
682
      in
 
683
      List.iter checkLabel s.labels;
 
684
      (* See if we have seen this statement before *)
 
685
      if List.memq s !statements then
 
686
        ignore (warn "Statement is shared");
 
687
      (* Remember that we have seen this one *)
 
688
      statements := s :: !statements;
 
689
      match s.skind with
 
690
        Break _ | Continue _ -> ()
 
691
      | Goto (gref, l) ->
 
692
          currentLoc := l;
 
693
          (* Find a label *)
 
694
          let lab =
 
695
            match List.filter (function Label _ -> true | _ -> false)
 
696
                  !gref.labels with
 
697
              Label (lab, _, _) :: _ -> lab
 
698
            | _ ->
 
699
                ignore (warn "Goto to block without a label\n");
 
700
                "<missing label>"
 
701
          in
 
702
          (* Remember it as a target *)
 
703
          gotoTargets := (lab, !gref) :: !gotoTargets
 
704
 
 
705
 
 
706
      | Return (re,l) -> begin
 
707
          currentLoc := l;
 
708
          match re, !currentReturnType with
 
709
            None, TVoid _  -> ()
 
710
          | _, TVoid _ -> ignore (warn "Invalid return value")
 
711
          | None, _ -> ignore (warn "Invalid return value")
 
712
          | Some re', rt' -> checkExpType false re' rt'
 
713
        end
 
714
      | Loop (_, b, l, _, _) -> checkBlock b
 
715
      | Block b -> checkBlock b
 
716
      | If (e, bt, bf, l) ->
 
717
          currentLoc := l;
 
718
          let te = checkExp false e in
 
719
          checkBooleanType te;
 
720
          checkBlock bt;
 
721
          checkBlock bf
 
722
      | Switch (e, b, cases, l) ->
 
723
          currentLoc := l;
 
724
          checkExpType false e intType;
 
725
          (* Remember the statements so far *)
 
726
          let prevStatements = !statements in
 
727
          checkBlock b;
 
728
          (* Now make sure that all the cases do occur in that block *)
 
729
          List.iter
 
730
            (fun c ->
 
731
              if not (List.exists (function Case _ -> true | _ -> false)
 
732
                        c.labels) then
 
733
                ignore (warn "Case in switch statment without a \"case\"\n");
 
734
              (* Make sure it is in there *)
 
735
              let rec findCase = function
 
736
                | l when l == prevStatements -> (* Not found *)
 
737
                    ignore (warnContext
 
738
                              "Cannot find target of switch statement")
 
739
                | [] -> E.s (E.bug "Check: findCase")
 
740
                | c' :: rest when c == c' -> () (* Found *)
 
741
                | _ :: rest -> findCase rest
 
742
              in
 
743
              findCase !statements)
 
744
            cases;
 
745
      | TryFinally (b, h, l) ->
 
746
          currentLoc := l;
 
747
          checkBlock b;
 
748
          checkBlock h
 
749
 
 
750
      | TryExcept (b, (il, e), h, l) ->
 
751
          currentLoc := l;
 
752
          checkBlock b;
 
753
          List.iter checkInstr il;
 
754
          checkExpType false e intType;
 
755
          checkBlock h
 
756
 
 
757
      | Instr i -> checkInstr i)
 
758
    () (* argument of withContext *)
 
759
 
 
760
and checkBlock (b: block) : unit =
 
761
  List.iter checkStmt b.bstmts
 
762
 
 
763
 
 
764
and checkInstr (i: instr) =
 
765
  match i with
 
766
  | Set (dest, e, l) ->
 
767
      currentLoc := l;
 
768
      let t = checkLval false dest in
 
769
      (* Not all types can be assigned to *)
 
770
      (match unrollType t with
 
771
        TFun _ -> ignore (warn "Assignment to a function type")
 
772
      | TArray _ -> ignore (warn "Assignment to an array type")
 
773
      | TVoid _ -> ignore (warn "Assignment to a void type")
 
774
      | _ -> ());
 
775
      checkExpType false e t
 
776
 
 
777
  | Call(dest, what, args, l) ->
 
778
      currentLoc := l;
 
779
      let (rt, formals, isva) =
 
780
        match checkExp false what with
 
781
          TFun(rt, formals, isva, _) -> rt, formals, isva
 
782
        | _ -> E.s (bug "Call to a non-function")
 
783
      in
 
784
          (* Now check the return value*)
 
785
      (match dest, unrollType rt with
 
786
        None, TVoid _ -> ()
 
787
      | Some _, TVoid _ -> ignore (warn "void value is assigned")
 
788
      | None, _ -> () (* "Call of function is not assigned" *)
 
789
      | Some destlv, rt' ->
 
790
          let desttyp = checkLval false destlv in
 
791
          if typeSig desttyp <> typeSig rt then begin
 
792
            (* Not all types can be assigned to *)
 
793
            (match unrollType desttyp with
 
794
              TFun _ -> ignore (warn "Assignment to a function type")
 
795
            | TArray _ -> ignore (warn "Assignment to an array type")
 
796
            | TVoid _ -> ignore (warn "Assignment to a void type")
 
797
            | _ -> ());
 
798
            (* Not all types can be cast *)
 
799
            (match rt' with
 
800
              TArray _ -> ignore (warn "Cast of an array type")
 
801
            | TFun _ -> ignore (warn "Cast of a function type")
 
802
            | TComp _ -> ignore (warn "Cast of a composite type")
 
803
            | TVoid _ -> ignore (warn "Cast of a void type")
 
804
 
 
805
            | _ -> ())
 
806
          end);
 
807
          (* Now check the arguments *)
 
808
      let rec loopArgs formals args =
 
809
        match formals, args with
 
810
          [], _ when (isva || args = []) -> ()
 
811
        | (fn,ft,_) :: formals, a :: args ->
 
812
            checkExpType false a ft;
 
813
            loopArgs formals args
 
814
        | _, _ -> ignore (warn "Not enough arguments")
 
815
      in
 
816
      if formals = None then
 
817
        ignore (warn "Call to function without prototype\n")
 
818
      else
 
819
        loopArgs (argsToList formals) args
 
820
 
 
821
  | Asm _ -> ()  (* Not yet implemented *)
 
822
 
 
823
  | Skip _ -> ()
 
824
 
 
825
  | Code_annot _ -> ()
 
826
 
 
827
let rec checkGlobal = function
 
828
    GAsm _ -> ()
 
829
  | GPragma _ -> ()
 
830
  | GText _ -> ()
 
831
  | GAnnot _ -> ()
 
832
  | GType (ti, l) ->
 
833
      currentLoc := l;
 
834
      E.withContext (fun _ -> dprintf "GType(%s)" ti.tname)
 
835
        (fun _ ->
 
836
          checkTypeInfo Defined ti;
 
837
          if ti.tname <> "" then defineName ti.tname)
 
838
        ()
 
839
 
 
840
  | GCompTag (comp, l) ->
 
841
      currentLoc := l;
 
842
      checkCompInfo Defined comp;
 
843
 
 
844
  | GCompTagDecl (comp, l) ->
 
845
      currentLoc := l;
 
846
      checkCompInfo Forward comp;
 
847
 
 
848
  | GEnumTag (enum, l) ->
 
849
      currentLoc := l;
 
850
      checkEnumInfo Defined enum
 
851
 
 
852
  | GEnumTagDecl (enum, l) ->
 
853
      currentLoc := l;
 
854
      checkEnumInfo Forward enum
 
855
 
 
856
  | GVarDecl (vi, l) ->
 
857
      currentLoc := l;
 
858
      (* We might have seen it already *)
 
859
      E.withContext
 
860
        (fun _ -> dprintf "GVarDecl(%s)" (!Cil.output_ident vi.vname))
 
861
        (fun _ ->
 
862
          (* If we have seen this vid already then it must be for the exact
 
863
           * same varinfo *)
 
864
          if H.mem varIdsEnv vi.vid then
 
865
            checkVariable vi
 
866
          else begin
 
867
            defineVariable vi;
 
868
            checkAttributes vi.vattr;
 
869
            checkType vi.vtype CTDecl;
 
870
            if not (vi.vglob &&
 
871
                    vi.vstorage <> Register) then
 
872
              E.s (bug "Invalid declaration of %s"
 
873
                     (!Cil.output_ident vi.vname))
 
874
          end)
 
875
        ()
 
876
 
 
877
  | GVar (vi, init, l) ->
 
878
      currentLoc := l;
 
879
      (* Maybe this is the first occurrence *)
 
880
      E.withContext (fun _ -> dprintf "GVar(%s)"
 
881
                       (!Cil.output_ident vi.vname))
 
882
        (fun _ ->
 
883
          checkGlobal (GVarDecl (vi, l));
 
884
          (* Check the initializer *)
 
885
          begin match init.init with
 
886
            None -> ()
 
887
          | Some i -> ignore (checkInitType i vi.vtype)
 
888
          end;
 
889
          (* Cannot be a function *)
 
890
          if isFunctionType vi.vtype then
 
891
            E.s (bug "GVar for a function (%s)\n"
 
892
                   (!Cil.output_ident vi.vname));
 
893
          )
 
894
        ()
 
895
 
 
896
 
 
897
  | GFun (fd, l) -> begin
 
898
      currentLoc := l;
 
899
      (* Check if this is the first occurrence *)
 
900
      let vi = fd.svar in
 
901
      let fname = vi.vname in
 
902
      E.withContext (fun _ -> dprintf "GFun(%s)" (!Cil.output_ident fname))
 
903
        (fun _ ->
 
904
          checkGlobal (GVarDecl (vi, l));
 
905
          (* Check that the argument types in the type are identical to the
 
906
           * formals *)
 
907
          let rec loopArgs targs formals =
 
908
            match targs, formals with
 
909
              [], [] -> ()
 
910
            | (fn, ft, fa) :: targs, fo :: formals ->
 
911
                if fn <> fo.vname || ft != fo.vtype || fa != fo.vattr then
 
912
                  ignore (warnContext
 
913
                            "Formal %s not shared (type + locals) in %s"
 
914
                            (!Cil.output_ident fo.vname)
 
915
                            (!Cil.output_ident fname));
 
916
                loopArgs targs formals
 
917
 
 
918
            | _ ->
 
919
                E.s (bug "Type has different number of formals for %s"
 
920
                       (!Cil.output_ident fname))
 
921
          in
 
922
          begin match vi.vtype with
 
923
            TFun (rt, args, isva, a) -> begin
 
924
              currentReturnType := rt;
 
925
              loopArgs (argsToList args) fd.sformals
 
926
            end
 
927
          | _ -> E.s (bug "Function %s does not have a function type"
 
928
                        (!Cil.output_ident fname))
 
929
          end;
 
930
          ignore (fd.smaxid >= 0 ||
 
931
                    E.s (bug "smaxid < 0 for %s" (!Cil.output_ident fname)));
 
932
          (* Now start a new environment, in a finally clause *)
 
933
          begin try
 
934
            startEnv ();
 
935
            (* Do the locals *)
 
936
            let doLocal tctx v =
 
937
              if v.vglob then
 
938
                ignore (warnContext
 
939
                          "Local %s has the vglob flag set"
 
940
                          (!Cil.output_ident v.vname));
 
941
              if v.vstorage <> NoStorage && v.vstorage <> Register then
 
942
                ignore (warnContext
 
943
                          "Local %s has storage %a\n"
 
944
                          (!Cil.output_ident v.vname)
 
945
                          d_storage v.vstorage);
 
946
              checkType v.vtype tctx;
 
947
              checkAttributes v.vattr;
 
948
              defineVariable v
 
949
            in
 
950
            List.iter (doLocal CTFArg) fd.sformals;
 
951
            List.iter (doLocal CTDecl) fd.slocals;
 
952
            statements := [];
 
953
            gotoTargets := [];
 
954
            checkBlock fd.sbody;
 
955
            H.clear labels;
 
956
            (* Now verify that we have scanned all targets *)
 
957
            List.iter
 
958
              (fun (lab, t) -> if not (List.memq t !statements) then
 
959
                ignore (warnContext
 
960
                          "Target of \"goto %s\" statement does not appear in function body" lab))
 
961
              !gotoTargets;
 
962
            statements := [];
 
963
            gotoTargets := [];
 
964
            (* Done *)
 
965
            endEnv ()
 
966
          with e ->
 
967
            endEnv ();
 
968
            raise e
 
969
          end;
 
970
          ())
 
971
        () (* final argument of withContext *)
 
972
  end
 
973
 
 
974
 
 
975
let checkFile flags fl =
 
976
  if !E.verboseFlag then ignore (E.log "Checking file %s\n" fl.fileName);
 
977
  valid := true;
 
978
  List.iter
 
979
    (function
 
980
        NoCheckGlobalIds -> checkGlobalIds := false)
 
981
    flags;
 
982
  iterGlobals fl (fun g -> try checkGlobal g with _ -> ());
 
983
  (* Check that for all struct/union tags there is a definition *)
 
984
  H.iter
 
985
    (fun k (comp, isadef) ->
 
986
      if !isadef = Used then
 
987
        begin
 
988
          valid := false;
 
989
          ignore (E.warn "Compinfo %s is referenced but not defined"
 
990
                    (compFullName comp))
 
991
        end)
 
992
    compUsed;
 
993
  (* Check that for all enum tags there is a definition *)
 
994
  H.iter
 
995
    (fun k (enum, isadef) ->
 
996
      if !isadef = Used then
 
997
        begin
 
998
          valid := false;
 
999
          ignore (E.warn "Enuminfo %s is referenced but not defined"
 
1000
                    enum.ename)
 
1001
        end)
 
1002
    enumUsed;
 
1003
  (* Clean the hashes to let the GC do its job *)
 
1004
  H.clear typeDefs;
 
1005
  H.clear varNamesEnv;
 
1006
  H.clear varIdsEnv;
 
1007
  H.clear allBases;
 
1008
  H.clear compNames;
 
1009
  H.clear compUsed;
 
1010
  H.clear enumUsed;
 
1011
  H.clear typUsed;
 
1012
  varNamesList := [];
 
1013
  if !E.verboseFlag then
 
1014
    ignore (E.log "Finished checking file %s\n" fl.fileName);
 
1015
  !valid