rosenberg: handle bit fields better
[smatch.git] / check_uninitialized.c
blob10787eb71e3bf78c7ecd0af178a24527e35754d1
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 void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
29 if (is_impossible_path())
30 set_state(my_id, cur->name, cur->sym, &initialized);
33 static void mark_members_uninitialized(struct symbol *sym)
35 struct symbol *struct_type, *tmp, *base_type;
36 char buf[256];
38 struct_type = get_real_base_type(sym);
39 FOR_EACH_PTR(struct_type->symbol_list, tmp) {
40 if (!tmp->ident)
41 continue;
42 base_type = get_real_base_type(tmp);
43 if (!base_type ||
44 base_type->type == SYM_STRUCT ||
45 base_type->type == SYM_ARRAY ||
46 base_type->type == SYM_UNION)
47 continue;
48 snprintf(buf, sizeof(buf), "%s.%s", sym->ident->name, tmp->ident->name);
49 set_state(my_id, buf, sym, &uninitialized);
50 } END_FOR_EACH_PTR(tmp);
53 static void match_declarations(struct symbol *sym)
55 struct symbol *type;
57 if (sym->initializer)
58 return;
60 type = get_real_base_type(sym);
61 /* Smatch is crap at tracking arrays */
62 if (type->type == SYM_ARRAY)
63 return;
64 if (type->type == SYM_UNION)
65 return;
66 if (sym->ctype.modifiers & MOD_STATIC)
67 return;
69 if (!sym->ident)
70 return;
72 if (type->type == SYM_STRUCT) {
73 mark_members_uninitialized(sym);
74 return;
77 set_state(my_id, sym->ident->name, sym, &uninitialized);
80 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
82 if (__in_fake_struct_assign)
83 return;
84 if (expr && expr->smatch_flags & Fake)
85 return;
86 if (!sym || !sym->ident)
87 return;
88 if (strcmp(name, sym->ident->name) != 0)
89 return;
90 set_state(my_id, name, sym, &initialized);
93 static void match_assign(struct expression *expr)
95 struct expression *right;
97 right = strip_expr(expr->right);
98 if (right->type == EXPR_PREOP && right->op == '&')
99 set_state_expr(my_id, right->unop, &initialized);
102 static void match_negative_comparison(struct expression *expr)
104 struct expression *success;
105 struct sm_state *sm;
106 sval_t max;
109 * In the kernel, people don't use "if (ret) {" and "if (ret < 0) {"
110 * consistently. Ideally Smatch would know the return but often it
111 * doesn't.
115 if (option_project != PROJ_KERNEL)
116 return;
118 if (expr->type != EXPR_COMPARE || expr->op != '<')
119 return;
120 if (!expr_is_zero(expr->right))
121 return;
122 if (get_implied_max(expr->left, &max) && max.value == 0)
123 return;
125 success = compare_expression(expr->left, SPECIAL_EQUAL, expr->right);
126 if (!assume(success))
127 return;
129 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
130 if (sm->state == &initialized)
131 set_true_false_states(my_id, sm->name, sm->sym, NULL, &initialized);
132 } END_FOR_EACH_SM(sm);
134 end_assume();
137 static int is_initialized(struct expression *expr)
139 struct sm_state *sm;
141 expr = strip_expr(expr);
142 if (expr->type != EXPR_SYMBOL)
143 return 1;
144 sm = get_sm_state_expr(my_id, expr);
145 if (!sm)
146 return 1;
147 if (!slist_has_state(sm->possible, &uninitialized))
148 return 1;
149 return 0;
152 static void match_dereferences(struct expression *expr)
154 char *name;
156 if (implications_off || parse_error)
157 return;
159 if (expr->type != EXPR_PREOP)
160 return;
161 if (is_impossible_path())
162 return;
163 if (is_initialized(expr->unop))
164 return;
166 name = expr_to_str(expr->unop);
167 sm_error("potentially dereferencing uninitialized '%s'.", name);
168 free_string(name);
170 set_state_expr(my_id, expr->unop, &initialized);
173 static void match_condition(struct expression *expr)
175 char *name;
177 if (implications_off || parse_error)
178 return;
180 if (is_impossible_path())
181 return;
183 if (is_initialized(expr))
184 return;
186 name = expr_to_str(expr);
187 sm_error("potentially using uninitialized '%s'.", name);
188 free_string(name);
190 set_state_expr(my_id, expr, &initialized);
193 static void match_call(struct expression *expr)
195 struct expression *arg;
196 char *name;
198 if (parse_error)
199 return;
201 if (is_impossible_path())
202 return;
204 FOR_EACH_PTR(expr->args, arg) {
205 if (is_initialized(arg))
206 continue;
208 name = expr_to_str(arg);
209 sm_warning("passing uninitialized '%s'", name);
210 free_string(name);
212 set_state_expr(my_id, arg, &initialized);
213 } END_FOR_EACH_PTR(arg);
216 static int param_used_callback(void *found, int argc, char **argv, char **azColName)
218 *(int *)found = 1;
219 return 0;
222 static int member_is_used(struct expression *call, int param, char *printed_name)
224 int found;
226 /* for function pointers assume everything is used */
227 if (call->fn->type != EXPR_SYMBOL)
228 return 0;
230 found = 0;
231 run_sql(&param_used_callback, &found,
232 "select * from return_implies where %s and type = %d and parameter = %d and key = '%s';",
233 get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name);
234 return found;
237 static void match_call_struct_members(struct expression *expr)
239 struct symbol *type, *sym;
240 struct expression *arg;
241 struct sm_state *sm;
242 char *arg_name;
243 char buf[256];
244 int param;
246 return;
248 if (parse_error)
249 return;
251 param = -1;
252 FOR_EACH_PTR(expr->args, arg) {
253 param++;
254 if (arg->type != EXPR_PREOP || arg->op != '&')
255 continue;
256 type = get_type(arg->unop);
257 if (!type || type->type != SYM_STRUCT)
258 continue;
259 arg_name = expr_to_var_sym(arg->unop, &sym);
260 if (!arg_name || !sym)
261 goto free;
262 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
263 if (sm->sym != sym)
264 continue;
265 if (!slist_has_state(sm->possible, &uninitialized))
266 continue;
267 snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1);
268 if (!member_is_used(expr, param, buf))
269 goto free;
270 sm_warning("struct member %s is uninitialized", sm->name);
271 } END_FOR_EACH_SM(sm);
273 free:
274 free_string(arg_name);
275 } END_FOR_EACH_PTR(arg);
278 static int is_being_modified(struct expression *expr)
280 struct expression *parent;
281 struct statement *stmt;
283 parent = expr_get_parent_expr(expr);
284 if (!parent)
285 return 0;
286 while (parent->type == EXPR_PREOP && parent->op == '(') {
287 parent = expr_get_parent_expr(parent);
288 if (!parent)
289 return 0;
291 if (parent->type == EXPR_PREOP && parent->op == '&')
292 return 1;
293 if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr))
294 return 1;
296 stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
297 if (stmt && stmt->type == STMT_ASM)
298 return 1;
300 return 0;
303 static bool is_just_silencing_used_variable(struct expression *expr)
305 struct symbol *type;
307 while ((expr = expr_get_parent_expr(expr))) {
308 if (expr->type == EXPR_PREOP && expr->op == '(')
309 continue;
310 if (expr->type == EXPR_CAST) {
311 type = expr->cast_type;
313 if (!type)
314 return false;
315 if (type->type == SYM_NODE)
316 type = get_real_base_type(type);
317 if (type == &void_ctype)
318 return true;
322 return false;
325 static void match_symbol(struct expression *expr)
327 char *name;
329 if (implications_off || parse_error)
330 return;
332 if (is_impossible_path())
333 return;
335 if (is_initialized(expr))
336 return;
338 if (is_being_modified(expr))
339 return;
341 if (is_just_silencing_used_variable(expr))
342 return;
344 name = expr_to_str(expr);
345 sm_error("uninitialized symbol '%s'.", name);
346 free_string(name);
348 set_state_expr(my_id, expr, &initialized);
351 static void match_untracked(struct expression *call, int param)
353 struct expression *arg;
355 arg = get_argument_from_call_expr(call->args, param);
356 arg = strip_expr(arg);
357 if (!arg || arg->type != EXPR_PREOP || arg->op != '&')
358 return;
359 arg = strip_expr(arg->unop);
360 set_state_expr(my_id, arg, &initialized);
363 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr)
365 int arg_nr = PTR_INT(_arg_nr);
366 struct expression *arg;
368 arg = get_argument_from_call_expr(expr->args, arg_nr);
369 arg = strip_expr(arg);
370 if (!arg)
371 return;
372 if (arg->type != EXPR_PREOP || arg->op != '&')
373 return;
374 arg = strip_expr(arg->unop);
375 set_state_expr(my_id, arg, &initialized);
378 static void register_ignored_params_from_file(void)
380 char name[256];
381 struct token *token;
382 const char *func;
383 char prev_func[256];
384 int param;
386 memset(prev_func, 0, sizeof(prev_func));
387 snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str);
388 name[255] = '\0';
389 token = get_tokens_file(name);
390 if (!token)
391 return;
392 if (token_type(token) != TOKEN_STREAMBEGIN)
393 return;
394 token = token->next;
395 while (token_type(token) != TOKEN_STREAMEND) {
396 if (token_type(token) != TOKEN_IDENT)
397 return;
398 func = show_ident(token->ident);
400 token = token->next;
401 if (token_type(token) != TOKEN_NUMBER)
402 return;
403 param = atoi(token->number);
405 add_function_hook(func, &match_ignore_param, INT_PTR(param));
407 token = token->next;
409 clear_token_alloc();
412 void check_uninitialized(int id)
414 my_id = id;
416 add_hook(&match_declarations, DECLARATION_HOOK);
417 add_extra_mod_hook(&extra_mod_hook);
418 add_hook(&match_assign, ASSIGNMENT_HOOK);
419 add_hook(&match_negative_comparison, CONDITION_HOOK);
420 add_untracked_param_hook(&match_untracked);
421 add_pre_merge_hook(my_id, &pre_merge_hook);
423 add_hook(&match_dereferences, DEREF_HOOK);
424 add_hook(&match_condition, CONDITION_HOOK);
425 add_hook(&match_call, FUNCTION_CALL_HOOK);
426 add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK);
427 add_hook(&match_symbol, SYM_HOOK);
429 register_ignored_params_from_file();