2 * Copyright (C) 2014 Oracle.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
19 #include "smatch_slist.h"
20 #include "smatch_extra.h"
27 static bool uncertain_code_path(void)
29 if (implications_off
|| parse_error
)
31 if (is_impossible_path())
37 static void pre_merge_hook(struct sm_state
*cur
, struct sm_state
*other
)
39 if (is_impossible_path())
40 set_state(my_id
, cur
->name
, cur
->sym
, &initialized
);
43 static void mark_members_uninitialized(struct symbol
*sym
)
45 struct symbol
*struct_type
, *tmp
, *base_type
;
48 struct_type
= get_real_base_type(sym
);
49 FOR_EACH_PTR(struct_type
->symbol_list
, tmp
) {
52 base_type
= get_real_base_type(tmp
);
54 base_type
->type
== SYM_STRUCT
||
55 base_type
->type
== SYM_ARRAY
||
56 base_type
->type
== SYM_UNION
)
58 snprintf(buf
, sizeof(buf
), "%s.%s", sym
->ident
->name
, tmp
->ident
->name
);
59 set_state(my_id
, buf
, sym
, &uninitialized
);
60 } END_FOR_EACH_PTR(tmp
);
63 static void match_declarations(struct symbol
*sym
)
73 type
= get_real_base_type(sym
);
74 /* Smatch is crap at tracking arrays */
75 if (type
->type
== SYM_ARRAY
)
77 if (type
->type
== SYM_UNION
)
79 if (sym
->ctype
.modifiers
& MOD_STATIC
)
85 if (type
->type
== SYM_STRUCT
) {
86 mark_members_uninitialized(sym
);
90 set_state(my_id
, sym
->ident
->name
, sym
, &uninitialized
);
93 static int is_initialized(struct expression
*expr
)
97 expr
= strip_expr(expr
);
98 if (expr
->type
!= EXPR_SYMBOL
)
100 sm
= get_sm_state_expr(my_id
, expr
);
103 if (!slist_has_state(sm
->possible
, &uninitialized
))
108 static void warn_about_special_assign(struct expression
*expr
)
112 if (!expr
|| expr
->type
!= EXPR_ASSIGNMENT
|| expr
->op
== '=')
115 if (uncertain_code_path())
118 if (is_initialized(expr
->left
))
121 name
= expr_to_str(expr
->left
);
122 sm_warning("uninitialized special assign '%s'", name
);
126 static void extra_mod_hook(const char *name
, struct symbol
*sym
, struct expression
*expr
, struct smatch_state
*state
)
128 struct expression
*parent
= expr_get_parent_expr(expr
);
133 if (__in_fake_struct_assign
&& parent
&&
134 parent
->type
== EXPR_ASSIGNMENT
&&
135 is_fake_call(parent
->right
))
137 if (expr
&& expr
->smatch_flags
& Fake
)
139 if (!sym
|| !sym
->ident
)
141 if (strcmp(name
, sym
->ident
->name
) != 0)
143 warn_about_special_assign(expr
);
144 set_state(my_id
, name
, sym
, &initialized
);
147 static void match_assign(struct expression
*expr
)
149 struct expression
*right
;
151 if (is_fake_var_assign(expr
))
154 right
= strip_expr(expr
->right
);
155 if (right
->type
== EXPR_PREOP
&& right
->op
== '&')
156 set_state_expr(my_id
, right
->unop
, &initialized
);
159 static void match_negative_comparison(struct expression
*expr
)
161 struct expression
*success
;
166 * In the kernel, people don't use "if (ret) {" and "if (ret < 0) {"
167 * consistently. Ideally Smatch would know the return but often it
172 if (option_project
!= PROJ_KERNEL
)
175 if (expr
->type
!= EXPR_COMPARE
|| expr
->op
!= '<')
177 if (!expr_is_zero(expr
->right
))
179 if (get_implied_max(expr
->left
, &max
) && max
.value
== 0)
182 success
= compare_expression(expr
->left
, SPECIAL_EQUAL
, expr
->right
);
183 if (!assume(success
))
186 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
187 if (sm
->state
== &initialized
)
188 set_true_false_states(my_id
, sm
->name
, sm
->sym
, NULL
, &initialized
);
189 } END_FOR_EACH_SM(sm
);
194 static struct statement
*clear_states
;
195 static void match_enum_switch(struct statement
*stmt
)
197 struct expression
*expr
;
200 if (stmt
->type
!= STMT_COMPOUND
)
202 stmt
= stmt_get_parent_stmt(stmt
);
203 if (!stmt
|| stmt
->type
!= STMT_SWITCH
)
206 /* This ended up way uglier than I imagined */
207 if (__has_default_case())
210 expr
= strip_expr(stmt
->switch_expression
);
212 if (!type
|| type
->type
!= SYM_ENUM
)
218 static void match_enum_switch_after(struct statement
*stmt
)
222 if (clear_states
!= stmt
)
225 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
226 if (sm
->state
== &merged
)
227 set_state(my_id
, sm
->name
, sm
->sym
, &initialized
);
228 } END_FOR_EACH_SM(sm
);
231 static void match_dereferences(struct expression
*expr
)
235 if (uncertain_code_path())
238 if (expr
->type
!= EXPR_PREOP
)
240 if (is_initialized(expr
->unop
))
243 name
= expr_to_str(expr
->unop
);
244 sm_error("potentially dereferencing uninitialized '%s'.", name
);
247 set_state_expr(my_id
, expr
->unop
, &initialized
);
250 static void match_condition(struct expression
*expr
)
254 if (uncertain_code_path())
257 if (is_initialized(expr
))
260 name
= expr_to_str(expr
);
261 sm_error("potentially using uninitialized '%s'.", name
);
264 set_state_expr(my_id
, expr
, &initialized
);
267 static void match_function_pointer_call(struct expression
*expr
)
269 struct expression
*parent
, *arg
, *tmp
;
272 * If you call a function pointer foo->read(&val) without checking
273 * for errors then you knew what you were doing when you wrote that
274 * code. I'm not the police to try to prevent intentional bugs.
277 parent
= expr_get_parent_expr(expr
);
280 if (expr
->fn
->type
== EXPR_SYMBOL
)
283 FOR_EACH_PTR(expr
->args
, arg
) {
284 tmp
= strip_expr(arg
);
285 if (tmp
->type
!= EXPR_PREOP
|| tmp
->op
!= '&')
287 set_state_expr(my_id
, tmp
->unop
, &initialized
);
288 } END_FOR_EACH_PTR(arg
);
291 static void match_call(struct expression
*expr
)
293 struct expression
*arg
;
296 if (uncertain_code_path())
299 FOR_EACH_PTR(expr
->args
, arg
) {
300 if (is_initialized(arg
))
303 name
= expr_to_str(arg
);
304 sm_warning("passing uninitialized '%s'", name
);
307 set_state_expr(my_id
, arg
, &initialized
);
308 } END_FOR_EACH_PTR(arg
);
311 static int param_used_callback(void *found
, int argc
, char **argv
, char **azColName
)
317 static int member_is_used(struct expression
*call
, int param
, char *printed_name
)
321 /* for function pointers assume everything is used */
322 if (call
->fn
->type
!= EXPR_SYMBOL
)
326 run_sql(¶m_used_callback
, &found
,
327 "select * from return_implies where %s and type = %d and parameter = %d and key = '%s';",
328 get_static_filter(call
->fn
->symbol
), PARAM_USED
, param
, printed_name
);
332 static void match_call_struct_members(struct expression
*expr
)
334 struct symbol
*type
, *sym
;
335 struct expression
*arg
;
347 FOR_EACH_PTR(expr
->args
, arg
) {
349 if (arg
->type
!= EXPR_PREOP
|| arg
->op
!= '&')
351 type
= get_type(arg
->unop
);
352 if (!type
|| type
->type
!= SYM_STRUCT
)
354 arg_name
= expr_to_var_sym(arg
->unop
, &sym
);
355 if (!arg_name
|| !sym
)
357 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
360 if (!slist_has_state(sm
->possible
, &uninitialized
))
362 snprintf(buf
, sizeof(buf
), "$->%s", sm
->name
+ strlen(arg_name
) + 1);
363 if (!member_is_used(expr
, param
, buf
))
365 sm_warning("struct member %s is uninitialized", sm
->name
);
366 } END_FOR_EACH_SM(sm
);
369 free_string(arg_name
);
370 } END_FOR_EACH_PTR(arg
);
373 static int is_being_modified(struct expression
*expr
)
375 struct expression
*parent
;
376 struct statement
*stmt
;
378 parent
= expr_get_parent_expr(expr
);
381 while (parent
->type
== EXPR_PREOP
&& parent
->op
== '(') {
382 parent
= expr_get_parent_expr(parent
);
386 if (parent
->type
== EXPR_PREOP
&& parent
->op
== '&')
388 if (parent
->type
== EXPR_ASSIGNMENT
&& expr_equiv(parent
->left
, expr
))
391 stmt
= last_ptr_list((struct ptr_list
*)big_statement_stack
);
392 if (stmt
&& stmt
->type
== STMT_ASM
)
398 static bool is_just_silencing_used_variable(struct expression
*expr
)
402 while ((expr
= expr_get_parent_expr(expr
))) {
403 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '(')
405 if (expr
->type
== EXPR_CAST
) {
406 type
= expr
->cast_type
;
410 if (type
->type
== SYM_NODE
)
411 type
= get_real_base_type(type
);
412 if (type
== &void_ctype
)
420 static void match_symbol(struct expression
*expr
)
424 if (uncertain_code_path())
427 if (is_initialized(expr
))
430 if (is_being_modified(expr
))
433 if (is_just_silencing_used_variable(expr
))
436 name
= expr_to_str(expr
);
437 sm_error("uninitialized symbol '%s'.", name
);
440 set_state_expr(my_id
, expr
, &initialized
);
443 static void match_untracked(struct expression
*call
, int param
)
445 struct expression
*arg
;
447 arg
= get_argument_from_call_expr(call
->args
, param
);
448 arg
= strip_expr(arg
);
449 if (!arg
|| arg
->type
!= EXPR_PREOP
|| arg
->op
!= '&')
451 arg
= strip_expr(arg
->unop
);
452 set_state_expr(my_id
, arg
, &initialized
);
455 static void match_ignore_param(const char *fn
, struct expression
*expr
, void *_arg_nr
)
457 int arg_nr
= PTR_INT(_arg_nr
);
458 struct expression
*arg
;
460 arg
= get_argument_from_call_expr(expr
->args
, arg_nr
);
461 arg
= strip_expr(arg
);
464 if (arg
->type
!= EXPR_PREOP
|| arg
->op
!= '&')
466 arg
= strip_expr(arg
->unop
);
467 set_state_expr(my_id
, arg
, &initialized
);
470 static void register_ignored_params_from_file(void)
478 memset(prev_func
, 0, sizeof(prev_func
));
479 snprintf(name
, 256, "%s.ignore_uninitialized_param", option_project_str
);
481 token
= get_tokens_file(name
);
484 if (token_type(token
) != TOKEN_STREAMBEGIN
)
487 while (token_type(token
) != TOKEN_STREAMEND
) {
488 if (token_type(token
) != TOKEN_IDENT
)
490 func
= show_ident(token
->ident
);
493 if (token_type(token
) != TOKEN_NUMBER
)
495 param
= atoi(token
->number
);
497 add_function_hook(func
, &match_ignore_param
, INT_PTR(param
));
504 void check_uninitialized(int id
)
508 add_hook(&match_declarations
, DECLARATION_HOOK
);
509 add_extra_mod_hook(&extra_mod_hook
);
510 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
511 add_hook(&match_negative_comparison
, CONDITION_HOOK
);
512 add_hook(&match_enum_switch
, STMT_HOOK_AFTER
);
513 add_hook(&match_enum_switch_after
, STMT_HOOK_AFTER
);
514 add_untracked_param_hook(&match_untracked
);
515 add_pre_merge_hook(my_id
, &pre_merge_hook
);
517 add_hook(&match_dereferences
, DEREF_HOOK
);
518 add_hook(&match_condition
, CONDITION_HOOK
);
519 add_hook(&match_call
, FUNCTION_CALL_HOOK
);
520 add_hook(&match_function_pointer_call
, FUNCTION_CALL_HOOK
);
521 add_hook(&match_call_struct_members
, FUNCTION_CALL_HOOK
);
522 add_hook(&match_symbol
, SYM_HOOK
);
524 register_ignored_params_from_file();