Newer
Older
(**************************************************************************)
(* *)
(* This file is part of Frama-C. *)
(* *)
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
(* 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). *)
(* *)
(**************************************************************************)
(* Creation of the initial state of abstract domains. *)
open Cil_types
open Eval
module type S = sig
type state
val initial_state : lib_entry:bool -> state or_bottom
val initial_state_with_formals :
lib_entry:bool -> kernel_function -> state or_bottom
val initialize_local_variable:
stmt -> varinfo -> Cil_types.init -> state -> state or_bottom
end
type padding_initialization = [
| `Initialized
| `Uninitialized
| `MaybeInitialized
]
(* There are two different options for locals and for globals variables:
a three-valued parameter of Eva for globals, and a boolean parameter of
the kernel for locals. Please don't ask. *)
let padding_initialization ~local : padding_initialization =
if local
then
if Kernel.InitializedPaddingLocals.get ()
then `Initialized else `Uninitialized
else
match Parameters.InitializationPaddingGlobals.get () with
| "yes" -> `Initialized
| "maybe" -> `MaybeInitialized
| "no" -> `Uninitialized
| _ -> assert false
(* Warn if the size is unknown. *)
let warn_unknown_size vi =
try
ignore (Cil.bitsSizeOf vi.vtype);
false
with Cil.SizeOfError (s, t)->
let pp fmt v = Format.fprintf fmt "variable '%a'" Printer.pp_varinfo v in
Self.warning ~once:true ~current:true
~wkey:Self.wkey_unknown_size
"@[during initialization@ of %a,@ size of@ type '%a'@ cannot be@ \
computed@ (%s)@]" pp vi Printer.pp_typ t s;
true
(* A bottom in any part of an initializer results in a bottom for the
whole initialization. Thus, the following monad raises an exception on a
bottom case; the exception is catched by the root initialization functions
to return a proper `Bottom. *)
exception Initialization_failed
let (>>>) t f = match t with
| `Bottom -> raise Initialization_failed
| `Value v -> f v
let counter = ref 0
module Make
(Domain: Abstract.Domain.External)
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
(Eva: Evaluation.S with type state = Domain.state
and type loc = Domain.location)
(Transfer: Transfer_stmt.S with type state = Domain.t)
= struct
incr counter;;
(* Evaluation in the top state: we do not want a location to depend on
other globals. *)
let lval_to_loc lval =
fst (Eva.lvaluate ~for_writing:false Domain.top lval)
>>> fun (_valuation, loc, _typ) -> loc
(* ------------------------- Apply initializer ---------------------------- *)
(* Conventions:
- functions in *_var_* act on the entire variables, and receive only
the corresponding varinfo
- other functions act on a lvalue, which they directly receive *)
(* Initializes an entire variable [vi], in particular padding bits,
according to [local] and [lib_entry] mode. *)
let initialize_var_padding ~local ~lib_entry vi state =
let lval = Cil.var vi in
match padding_initialization ~local with
| `Uninitialized -> state
| `Initialized | `MaybeInitialized as i ->
let initialized = i = `Initialized in
let init_value =
if not local && lib_entry
then Abstract_domain.Top
else Abstract_domain.Zero
in
let location = lval_to_loc lval in
Domain.initialize_variable lval location ~initialized init_value state
(* Initializes a volatile lvalue to top. *)
let initialize_top_volatile lval state =
let location = lval_to_loc lval in
let init_value = Abstract_domain.Top in
Domain.initialize_variable lval location ~initialized:true init_value state
(* Applies a single Cil initializer, using the standard transfer function on
assignments. Warns if the results is bottom. *)
let apply_cil_single_initializer kinstr state lval expr =
match Transfer.assign state kinstr lval expr with
| `Bottom ->
if kinstr = Kglobal then
Self.warning ~once:true ~source:(fst expr.eloc)
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
"evaluation of initializer '%a' failed@." Printer.pp_exp expr;
raise Initialization_failed
| `Value v -> v
(* Applies an initializer. If [top_volatile] is true, sets volatile locations
to top without applying the initializer. Otherwise, lets the standard
transfer function on assignments handle volatile locations. *)
let rec apply_cil_initializer ~top_volatile kinstr lval init state =
if top_volatile && Cil.typeHasQualifier "volatile" (Cil.typeOfLval lval)
then initialize_top_volatile lval state
else
match init with
| SingleInit exp -> apply_cil_single_initializer kinstr state lval exp
| CompoundInit (typ, l) ->
let doinit off init _typ state =
let lval = Cil.addOffsetLval off lval in
apply_cil_initializer ~top_volatile kinstr lval init state
in
Cil.foldLeftCompound ~implicit:false ~doinit ~ct:typ ~initl:l ~acc:state
(* Field by field initialization of a variable to zero, or top if volatile.
Very inefficient. *)
let initialize_var_zero_or_volatile kinstr vi state =
let loc = Cil_datatype.Location.unknown in
let zero_init = Cil.makeZeroInit ~loc vi.vtype in
apply_cil_initializer ~top_volatile:true kinstr (Cil.var vi) zero_init state
(* ----------------------- Non Lib-entry mode ----------------------------- *)
(* Initializes a varinfo, padding bits + optionaly an initializer. *)
let initialize_var_not_lib_entry kinstr ~local vi init state =
ignore (warn_unknown_size vi);
let typ = vi.vtype in
let lval = Cil.var vi in
let volatile_everywhere = Cil.typeHasQualifier "volatile" typ in
let state =
if volatile_everywhere && padding_initialization ~local = `Initialized
then initialize_top_volatile lval state
else
(* Initializes padding bits everywhere (non padding bits are overwritten
afterwards). *)
let state = initialize_var_padding vi ~local ~lib_entry:false state in
(* Initializes everything except padding bits: non-volatile locations
to zero, volatile locations to top. We only do so if the variable
must be different from zero somewhere. This is a not-so minor
optimization. *)
if padding_initialization ~local = `Initialized &&
not (Cil.isVolatileType typ)
then state
else initialize_var_zero_or_volatile kinstr vi state
in
(* Applies the real initializer on top. *)
match init with
| None -> state
| Some init ->
apply_cil_initializer ~top_volatile:false kinstr lval init state
(* --------------------------- Lib-entry mode ----------------------------- *)
(* Special application of an initializer: only non-volatile lval with
attributes 'const' are initialized. *)
let rec apply_cil_const_initializer kinstr state lval = function
| SingleInit exp ->
let typ_lval = Cil.typeOfLval lval in
if Cil.typeHasQualifier "const" typ_lval &&
not (Cil.typeHasQualifier "volatile" typ_lval)
&& not (Cil.is_mutable_or_initialized lval)
then apply_cil_single_initializer kinstr state lval exp
else state
| CompoundInit (typ, l) ->
if Cil.typeHasQualifier "volatile" typ || not (Cil.isConstType typ)
then state (* initializer is not useful *)
else
let doinit off init _typ state =
apply_cil_const_initializer
kinstr state (Cil.addOffsetLval off lval) init
in
Cil.foldLeftCompound ~implicit:true ~doinit ~ct:typ ~initl:l ~acc:state
(* Initializes [vi] as if in [-lib-entry] mode. Active when [-lib-entry] is
set, or when [vi] is extern. [const] initializers, explicit or implicit,
are taken into account *)
let initialize_var_lib_entry kinstr vi init state =
if Cil.typeHasQualifier "const" vi.vtype && not (vi.vstorage = Extern)
&& not (Cil.typeHasAttributeMemoryBlock Cil.frama_c_mutable vi.vtype)
then (* Fully const base. Ignore -lib-entry altogether. *)
initialize_var_not_lib_entry kinstr ~local:false vi init state
else
let unknown_size = warn_unknown_size vi in
let state =
if unknown_size then
(* the type is unknown, initialize everything to Top *)
let lval = Cil.var vi in
let loc = lval_to_loc lval in
let v = Abstract_domain.Top in
Domain.initialize_variable lval loc ~initialized:true v state
else
(* Add padding everywhere. *)
let state =
initialize_var_padding vi ~local:false ~lib_entry:true state
in
(* Then initialize non-padding bits according to the type. *)
let kind = Abstract_domain.Global in
Domain.initialize_variable_using_type kind vi state
in
(* If needed, initializes const fields according to the initializer
(or generate one if there are none). In the first phase, they have been
set to generic values. *)
if Cil.isConstType vi.vtype && not (vi.vstorage = Extern)
then
let init = match init with
| None -> Cil.makeZeroInit ~loc:vi.vdecl vi.vtype
| Some init -> init
in
apply_cil_const_initializer kinstr state (Cil.var vi) init
else state
(* ------------- Adds formal argument of the main function --------------- *)
(* Compute values for the formals of [kf] (as if those were variables in
lib-entry mode) and add them to [state] *)
let compute_main_formals kf state =
match kf.fundec with
| Declaration (_, _, None, _) -> state
| Declaration (_, _, Some l, _)
| Definition ({ sformals = l }, _) ->
if l <> [] && Parameters.InterpreterMode.get ()
Self.abort "Entry point %a has arguments"
Kernel_function.pretty kf
else
David Bühler
committed
let var_kind = Abstract_domain.Formal kf in
let state = Domain.enter_scope var_kind l state in
List.fold_right (Domain.initialize_variable_using_type var_kind) l state
(* Use the values supplied in [actuals] for the formals of [kf], and
bind them in [state] *)
let add_supplied_main_formals kf actuals state =
David Bühler
committed
match Domain.get_cvalue with
| None -> Self.abort "Function Db.Value.fun_set_args cannot be \
| Some get_cvalue ->
let formals = Kernel_function.get_formals kf in
if (List.length formals) <> List.length actuals then
raise Db.Value.Incorrect_number_of_arguments;
David Bühler
committed
let cvalue_state = get_cvalue state in
let add_actual state actual formal =
let actual = Eval_op.offsetmap_of_v ~typ:formal.vtype actual in
Cvalue.Model.add_base (Base.of_varinfo formal) actual state
in
let cvalue_state =
List.fold_left2 add_actual cvalue_state actuals formals
in
let set_domain = Domain.set Cvalue_domain.State.key in
David Bühler
committed
set_domain (cvalue_state, Locals_scoping.bottom ()) state
let add_main_formals kf state =
match Db.Value.fun_get_args () with
| None -> compute_main_formals kf state
| Some actuals -> add_supplied_main_formals kf actuals state
(* ------------------------ High-level functions -------------------------- *)
let initialize_local_variable stmt vi init state =
try
`Value
(initialize_var_not_lib_entry
(Kstmt stmt) ~local:true vi (Some init) state)
with Initialization_failed -> `Bottom
let initialize_global_variable ~lib_entry vi init state =
Cil.CurrentLoc.set vi.vdecl;
David Bühler
committed
let state = Domain.enter_scope Abstract_domain.Global [vi] state in
if vi.vsource then
let initialize =
if lib_entry || (vi.vstorage = Extern)
then initialize_var_lib_entry
else initialize_var_not_lib_entry ~local:false
in
initialize Kglobal vi init.init state
else state
(* Compute the initial state with all global variable initialized. *)
let compute_global_state ~lib_entry () =
Self.debug ~level:2 "Computing globals values";
let state = Domain.empty () in
let initialize = initialize_global_variable ~lib_entry in
try `Value (Globals.Vars.fold_in_file_order initialize state)
with Initialization_failed -> `Bottom
(* Dependencies for the Frama-C states containing the initial states
of Eva: all correctness parameters of Eva, plus the AST itself. We
cannot use [Db.Value.self] directly, because we do not want to
depend on the tuning parameters. Previously, we use a more
fine-grained list, but this lead to bugs. See mantis #2277. *)
let correctness_deps =
Ast.self ::
List.map
(fun p -> State.get p.Typed_parameter.name)
Parameters.parameters_correctness
module InitialState =
State_builder.Option_ref
(Bottom.Make_Datatype (Domain))
(struct
let name = "Value.Initialization" ^ "(" ^ string_of_int !counter ^ ")"
let dependencies = correctness_deps
end)
let () = Ast.add_monotonic_state InitialState.self
(* The computation depends on the lib_entry option, which is a corrrectness
parameter of the analyzer: the InitialState memoization is thus safely
cleaned when lib_entry changes. *)
let global_state ~lib_entry =
InitialState.memo (compute_global_state ~lib_entry)
(* The global cvalue state may be supplied by the user. *)
let supplied_state () =
let cvalue_state = Db.Value.globals_state () in
if Cvalue.Model.is_reachable cvalue_state
then
David Bühler
committed
let cvalue_state = cvalue_state, Locals_scoping.bottom () in
`Value (Domain.set Cvalue_domain.State.key cvalue_state Domain.top)
else `Bottom
let initial_state ~lib_entry =
if Db.Value.globals_use_supplied_state ()
then supplied_state ()
else global_state ~lib_entry
let print_initial_cvalue_state state =
David Bühler
committed
let cvalue_state = Domain.get_cvalue_or_bottom state in
(* Do not show variables from the frama-c libc specifications. *)
let print_base base =
try
let varinfo = Base.to_varinfo base in
not (Cil.is_in_libc varinfo.vattr)
with Base.Not_a_C_variable -> true
in
let cvalue_state =
if Kernel.PrintLibc.get ()
then cvalue_state
else Cvalue.Model.filter_base print_base cvalue_state
in
Self.printf ~dkey:Self.dkey_initial_state
~header:(fun fmt -> Format.pp_print_string fmt
"Values of globals at initialization")
"@[ %a@]" Cvalue.Model.pretty cvalue_state
let initial_state_with_formals ~lib_entry kf =
let init_state =
if Db.Value.globals_use_supplied_state ()
then begin
Self.feedback "Initial state supplied by user";
supplied_state ()
end
else begin
Self.feedback "Computing initial state";
let state = global_state ~lib_entry in
Self.feedback "Initial state computed";
state
end
in
let b = Parameters.ResultsAll.get () in
Domain.Store.register_global_state b init_state;