unwind: ignore path states in pre-merge hook
[smatch.git] / check_uninitialized.c
blob0b670b26d27cea4d447e8049d44428d54973c364
1 /*
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
18 #include "smatch.h"
19 #include "smatch_slist.h"
20 #include "smatch_extra.h"
22 static int my_id;
24 STATE(uninitialized);
25 STATE(initialized);
27 static bool uncertain_code_path(void)
29 if (implications_off || parse_error)
30 return true;
31 if (is_impossible_path())
32 return true;
34 return false;
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;
46 char buf[256];
48 struct_type = get_real_base_type(sym);
49 FOR_EACH_PTR(struct_type->symbol_list, tmp) {
50 if (!tmp->ident)
51 continue;
52 base_type = get_real_base_type(tmp);
53 if (!base_type ||
54 base_type->type == SYM_STRUCT ||
55 base_type->type == SYM_ARRAY ||
56 base_type->type == SYM_UNION)
57 continue;
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)
65 struct symbol *type;
67 if (!cur_func_sym)
68 return;
70 if (sym->initializer)
71 return;
73 type = get_real_base_type(sym);
74 /* Smatch is crap at tracking arrays */
75 if (type->type == SYM_ARRAY)
76 return;
77 if (type->type == SYM_UNION)
78 return;
79 if (sym->ctype.modifiers & MOD_STATIC)
80 return;
82 if (!sym->ident)
83 return;
85 if (type->type == SYM_STRUCT) {
86 mark_members_uninitialized(sym);
87 return;
90 set_state(my_id, sym->ident->name, sym, &uninitialized);
93 static int is_initialized(struct expression *expr)
95 struct sm_state *sm;
97 expr = strip_expr(expr);
98 if (expr->type != EXPR_SYMBOL)
99 return 1;
100 sm = get_sm_state_expr(my_id, expr);
101 if (!sm)
102 return 1;
103 if (!slist_has_state(sm->possible, &uninitialized))
104 return 1;
105 return 0;
108 static void warn_about_special_assign(struct expression *expr)
110 char *name;
112 if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op == '=')
113 return;
115 if (uncertain_code_path())
116 return;
118 if (is_initialized(expr->left))
119 return;
121 name = expr_to_str(expr->left);
122 sm_warning("uninitialized special assign '%s'", name);
123 free_string(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);
130 if (!cur_func_sym)
131 return;
133 if (__in_fake_struct_assign && parent &&
134 parent->type == EXPR_ASSIGNMENT &&
135 is_fake_call(parent->right))
136 return;
137 if (expr && expr->smatch_flags & Fake)
138 return;
139 if (!sym || !sym->ident)
140 return;
141 if (strcmp(name, sym->ident->name) != 0)
142 return;
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))
152 return;
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;
162 struct sm_state *sm;
163 sval_t max;
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
168 * doesn't.
172 if (option_project != PROJ_KERNEL)
173 return;
175 if (expr->type != EXPR_COMPARE || expr->op != '<')
176 return;
177 if (!expr_is_zero(expr->right))
178 return;
179 if (get_implied_max(expr->left, &max) && max.value == 0)
180 return;
182 success = compare_expression(expr->left, SPECIAL_EQUAL, expr->right);
183 if (!assume(success))
184 return;
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);
191 end_assume();
194 static struct statement *clear_states;
195 static void match_enum_switch(struct statement *stmt)
197 struct expression *expr;
198 struct symbol *type;
200 if (stmt->type != STMT_COMPOUND)
201 return;
202 stmt = stmt_get_parent_stmt(stmt);
203 if (!stmt || stmt->type != STMT_SWITCH)
204 return;
206 /* This ended up way uglier than I imagined */
207 if (__has_default_case())
208 return;
210 expr = strip_expr(stmt->switch_expression);
211 type = expr->ctype;
212 if (!type || type->type != SYM_ENUM)
213 return;
215 clear_states = stmt;
218 static void match_enum_switch_after(struct statement *stmt)
220 struct sm_state *sm;
222 if (clear_states != stmt)
223 return;
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)
233 char *name;
235 if (uncertain_code_path())
236 return;
238 if (expr->type != EXPR_PREOP)
239 return;
240 if (is_initialized(expr->unop))
241 return;
243 name = expr_to_str(expr->unop);
244 sm_error("potentially dereferencing uninitialized '%s'.", name);
245 free_string(name);
247 set_state_expr(my_id, expr->unop, &initialized);
250 static void match_condition(struct expression *expr)
252 char *name;
254 if (uncertain_code_path())
255 return;
257 if (is_initialized(expr))
258 return;
260 name = expr_to_str(expr);
261 sm_error("potentially using uninitialized '%s'.", name);
262 free_string(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);
278 if (parent)
279 return;
280 if (expr->fn->type == EXPR_SYMBOL)
281 return;
283 FOR_EACH_PTR(expr->args, arg) {
284 tmp = strip_expr(arg);
285 if (tmp->type != EXPR_PREOP || tmp->op != '&')
286 continue;
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;
294 char *name;
296 if (uncertain_code_path())
297 return;
299 FOR_EACH_PTR(expr->args, arg) {
300 if (is_initialized(arg))
301 continue;
303 name = expr_to_str(arg);
304 sm_warning("passing uninitialized '%s'", name);
305 free_string(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)
313 *(int *)found = 1;
314 return 0;
317 static int member_is_used(struct expression *call, int param, char *printed_name)
319 int found;
321 /* for function pointers assume everything is used */
322 if (call->fn->type != EXPR_SYMBOL)
323 return 0;
325 found = 0;
326 run_sql(&param_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);
329 return found;
332 static void match_call_struct_members(struct expression *expr)
334 struct symbol *type, *sym;
335 struct expression *arg;
336 struct sm_state *sm;
337 char *arg_name;
338 char buf[256];
339 int param;
341 return;
343 if (parse_error)
344 return;
346 param = -1;
347 FOR_EACH_PTR(expr->args, arg) {
348 param++;
349 if (arg->type != EXPR_PREOP || arg->op != '&')
350 continue;
351 type = get_type(arg->unop);
352 if (!type || type->type != SYM_STRUCT)
353 continue;
354 arg_name = expr_to_var_sym(arg->unop, &sym);
355 if (!arg_name || !sym)
356 goto free;
357 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
358 if (sm->sym != sym)
359 continue;
360 if (!slist_has_state(sm->possible, &uninitialized))
361 continue;
362 snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1);
363 if (!member_is_used(expr, param, buf))
364 goto free;
365 sm_warning("struct member %s is uninitialized", sm->name);
366 } END_FOR_EACH_SM(sm);
368 free:
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);
379 if (!parent)
380 return 0;
381 while (parent->type == EXPR_PREOP && parent->op == '(') {
382 parent = expr_get_parent_expr(parent);
383 if (!parent)
384 return 0;
386 if (parent->type == EXPR_PREOP && parent->op == '&')
387 return 1;
388 if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr))
389 return 1;
391 stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
392 if (stmt && stmt->type == STMT_ASM)
393 return 1;
395 return 0;
398 static bool is_just_silencing_used_variable(struct expression *expr)
400 struct symbol *type;
402 while ((expr = expr_get_parent_expr(expr))) {
403 if (expr->type == EXPR_PREOP && expr->op == '(')
404 continue;
405 if (expr->type == EXPR_CAST) {
406 type = expr->cast_type;
408 if (!type)
409 return false;
410 if (type->type == SYM_NODE)
411 type = get_real_base_type(type);
412 if (type == &void_ctype)
413 return true;
417 return false;
420 static void match_symbol(struct expression *expr)
422 char *name;
424 if (uncertain_code_path())
425 return;
427 if (is_initialized(expr))
428 return;
430 if (is_being_modified(expr))
431 return;
433 if (is_just_silencing_used_variable(expr))
434 return;
436 name = expr_to_str(expr);
437 sm_error("uninitialized symbol '%s'.", name);
438 free_string(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 != '&')
450 return;
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);
462 if (!arg)
463 return;
464 if (arg->type != EXPR_PREOP || arg->op != '&')
465 return;
466 arg = strip_expr(arg->unop);
467 set_state_expr(my_id, arg, &initialized);
470 static void register_ignored_params_from_file(void)
472 char name[256];
473 struct token *token;
474 const char *func;
475 char prev_func[256];
476 int param;
478 memset(prev_func, 0, sizeof(prev_func));
479 snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str);
480 name[255] = '\0';
481 token = get_tokens_file(name);
482 if (!token)
483 return;
484 if (token_type(token) != TOKEN_STREAMBEGIN)
485 return;
486 token = token->next;
487 while (token_type(token) != TOKEN_STREAMEND) {
488 if (token_type(token) != TOKEN_IDENT)
489 return;
490 func = show_ident(token->ident);
492 token = token->next;
493 if (token_type(token) != TOKEN_NUMBER)
494 return;
495 param = atoi(token->number);
497 add_function_hook(func, &match_ignore_param, INT_PTR(param));
499 token = token->next;
501 clear_token_alloc();
504 void check_uninitialized(int id)
506 my_id = 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();