check_free_strict: New stricter cross function use after free check
[smatch.git] / check_uninitialized.c
blob84cbe04ad05fe57c210bced2bf482724cc0ccf36
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 *sm)
29 if (is_impossible_path())
30 set_state(my_id, sm->name, sm->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 (!sym || !sym->ident)
83 return;
84 if (strcmp(name, sym->ident->name) != 0)
85 return;
86 set_state(my_id, name, sym, &initialized);
89 static void match_assign(struct expression *expr)
91 struct expression *right;
93 right = strip_expr(expr->right);
94 if (right->type == EXPR_PREOP && right->op == '&')
95 set_state_expr(my_id, right->unop, &initialized);
98 static int is_initialized(struct expression *expr)
100 struct sm_state *sm;
102 expr = strip_expr(expr);
103 if (expr->type != EXPR_SYMBOL)
104 return 1;
105 sm = get_sm_state_expr(my_id, expr);
106 if (!sm)
107 return 1;
108 if (!slist_has_state(sm->possible, &uninitialized))
109 return 1;
110 return 0;
113 static void match_dereferences(struct expression *expr)
115 char *name;
117 if (parse_error)
118 return;
120 if (expr->type != EXPR_PREOP)
121 return;
122 if (is_impossible_path())
123 return;
124 if (is_initialized(expr->unop))
125 return;
127 name = expr_to_str(expr->unop);
128 sm_msg("error: potentially dereferencing uninitialized '%s'.", name);
129 free_string(name);
131 set_state_expr(my_id, expr->unop, &initialized);
134 static void match_condition(struct expression *expr)
136 char *name;
138 if (parse_error)
139 return;
141 if (is_impossible_path())
142 return;
144 if (is_initialized(expr))
145 return;
147 name = expr_to_str(expr);
148 sm_msg("error: potentially using uninitialized '%s'.", name);
149 free_string(name);
151 set_state_expr(my_id, expr, &initialized);
154 static void match_call(struct expression *expr)
156 struct expression *arg;
157 char *name;
159 if (parse_error)
160 return;
162 if (is_impossible_path())
163 return;
165 FOR_EACH_PTR(expr->args, arg) {
166 if (is_initialized(arg))
167 continue;
169 name = expr_to_str(arg);
170 sm_msg("warn: passing uninitialized '%s'", name);
171 free_string(name);
173 set_state_expr(my_id, arg, &initialized);
174 } END_FOR_EACH_PTR(arg);
177 static int param_used_callback(void *found, int argc, char **argv, char **azColName)
179 *(int *)found = 1;
180 return 0;
183 static int member_is_used(struct expression *call, int param, char *printed_name)
185 int found;
187 /* for function pointers assume everything is used */
188 if (call->fn->type != EXPR_SYMBOL)
189 return 0;
191 found = 0;
192 run_sql(&param_used_callback, &found,
193 "select * from call_implies where %s and type = %d and parameter = %d and key = '%s';",
194 get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name);
195 return found;
198 static void match_call_struct_members(struct expression *expr)
200 struct symbol *type, *sym;
201 struct expression *arg;
202 struct sm_state *sm;
203 char *arg_name;
204 char buf[256];
205 int param;
207 return;
209 if (parse_error)
210 return;
212 param = -1;
213 FOR_EACH_PTR(expr->args, arg) {
214 param++;
215 if (arg->type != EXPR_PREOP || arg->op != '&')
216 continue;
217 type = get_type(arg->unop);
218 if (!type || type->type != SYM_STRUCT)
219 continue;
220 arg_name = expr_to_var_sym(arg->unop, &sym);
221 if (!arg_name || !sym)
222 goto free;
223 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
224 if (sm->sym != sym)
225 continue;
226 if (!slist_has_state(sm->possible, &uninitialized))
227 continue;
228 snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1);
229 if (!member_is_used(expr, param, buf))
230 goto free;
231 sm_msg("warn: struct member %s is uninitialized", sm->name);
232 } END_FOR_EACH_SM(sm);
234 free:
235 free_string(arg_name);
236 } END_FOR_EACH_PTR(arg);
239 static int is_being_modified(struct expression *expr)
241 struct expression *parent;
242 struct statement *stmt;
244 parent = expr_get_parent_expr(expr);
245 if (!parent)
246 return 0;
247 while (parent->type == EXPR_PREOP && parent->op == '(')
248 parent = expr_get_parent_expr(parent);
249 if (parent->type == EXPR_PREOP && parent->op == '&')
250 return 1;
251 if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr))
252 return 1;
254 stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
255 if (stmt && stmt->type == STMT_ASM)
256 return 1;
258 return 0;
261 static void match_symbol(struct expression *expr)
263 char *name;
265 if (parse_error)
266 return;
268 if (is_impossible_path())
269 return;
271 if (is_initialized(expr))
272 return;
274 if (is_being_modified(expr))
275 return;
277 name = expr_to_str(expr);
278 sm_msg("error: uninitialized symbol '%s'.", name);
279 free_string(name);
281 set_state_expr(my_id, expr, &initialized);
284 static void match_untracked(struct expression *call, int param)
286 struct expression *arg;
288 arg = get_argument_from_call_expr(call->args, param);
289 arg = strip_expr(arg);
290 if (!arg || arg->type != EXPR_PREOP || arg->op != '&')
291 return;
292 arg = strip_expr(arg->unop);
293 set_state_expr(my_id, arg, &initialized);
296 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr)
298 int arg_nr = PTR_INT(_arg_nr);
299 struct expression *arg;
301 arg = get_argument_from_call_expr(expr->args, arg_nr);
302 arg = strip_expr(arg);
303 if (!arg)
304 return;
305 if (arg->type != EXPR_PREOP || arg->op != '&')
306 return;
307 arg = strip_expr(arg->unop);
308 set_state_expr(my_id, arg, &initialized);
311 static void register_ignored_params_from_file(void)
313 char name[256];
314 struct token *token;
315 const char *func;
316 char prev_func[256];
317 int param;
319 memset(prev_func, 0, sizeof(prev_func));
320 snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str);
321 name[255] = '\0';
322 token = get_tokens_file(name);
323 if (!token)
324 return;
325 if (token_type(token) != TOKEN_STREAMBEGIN)
326 return;
327 token = token->next;
328 while (token_type(token) != TOKEN_STREAMEND) {
329 if (token_type(token) != TOKEN_IDENT)
330 return;
331 func = show_ident(token->ident);
333 token = token->next;
334 if (token_type(token) != TOKEN_NUMBER)
335 return;
336 param = atoi(token->number);
338 add_function_hook(func, &match_ignore_param, INT_PTR(param));
340 token = token->next;
342 clear_token_alloc();
345 void check_uninitialized(int id)
347 my_id = id;
349 add_hook(&match_declarations, DECLARATION_HOOK);
350 add_extra_mod_hook(&extra_mod_hook);
351 add_hook(&match_assign, ASSIGNMENT_HOOK);
352 add_untracked_param_hook(&match_untracked);
353 add_pre_merge_hook(my_id, &pre_merge_hook);
355 add_hook(&match_dereferences, DEREF_HOOK);
356 add_hook(&match_condition, CONDITION_HOOK);
357 add_hook(&match_call, FUNCTION_CALL_HOOK);
358 add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK);
359 add_hook(&match_symbol, SYM_HOOK);
361 register_ignored_params_from_file();