xref: /illumos-gate/usr/src/tools/smatch/src/check_uninitialized.c (revision 1b58875ad7966cf2c85ee8e92f3da04f0a3b2f7a)
1 /*
2  * Copyright (C) 2014 Oracle.
3  *
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.
8  *
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.
13  *
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
16  */
17 
18 #include "smatch.h"
19 #include "smatch_slist.h"
20 #include "smatch_extra.h"
21 
22 static int my_id;
23 
24 STATE(uninitialized);
25 STATE(initialized);
26 
27 static void pre_merge_hook(struct sm_state *sm)
28 {
29 	if (is_impossible_path())
30 		set_state(my_id, sm->name, sm->sym, &initialized);
31 }
32 
33 static void mark_members_uninitialized(struct symbol *sym)
34 {
35 	struct symbol *struct_type, *tmp, *base_type;
36 	char buf[256];
37 
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);
51 }
52 
53 static void match_declarations(struct symbol *sym)
54 {
55 	struct symbol *type;
56 
57 	if (sym->initializer)
58 		return;
59 
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;
68 
69 	if (!sym->ident)
70 		return;
71 
72 	if (type->type == SYM_STRUCT) {
73 		mark_members_uninitialized(sym);
74 		return;
75 	}
76 
77 	set_state(my_id, sym->ident->name, sym, &uninitialized);
78 }
79 
80 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
81 {
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);
87 }
88 
89 static void match_assign(struct expression *expr)
90 {
91 	struct expression *right;
92 
93 	right = strip_expr(expr->right);
94 	if (right->type == EXPR_PREOP && right->op == '&')
95 		set_state_expr(my_id, right->unop, &initialized);
96 }
97 
98 static int is_initialized(struct expression *expr)
99 {
100 	struct sm_state *sm;
101 
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;
111 }
112 
113 static void match_dereferences(struct expression *expr)
114 {
115 	char *name;
116 
117 	if (parse_error)
118 		return;
119 
120 	if (expr->type != EXPR_PREOP)
121 		return;
122 	if (is_impossible_path())
123 		return;
124 	if (is_initialized(expr->unop))
125 		return;
126 
127 	name = expr_to_str(expr->unop);
128 	sm_error("potentially dereferencing uninitialized '%s'.", name);
129 	free_string(name);
130 
131 	set_state_expr(my_id, expr->unop, &initialized);
132 }
133 
134 static void match_condition(struct expression *expr)
135 {
136 	char *name;
137 
138 	if (parse_error)
139 		return;
140 
141 	if (is_impossible_path())
142 		return;
143 
144 	if (is_initialized(expr))
145 		return;
146 
147 	name = expr_to_str(expr);
148 	sm_error("potentially using uninitialized '%s'.", name);
149 	free_string(name);
150 
151 	set_state_expr(my_id, expr, &initialized);
152 }
153 
154 static void match_call(struct expression *expr)
155 {
156 	struct expression *arg;
157 	char *name;
158 
159 	if (parse_error)
160 		return;
161 
162 	if (is_impossible_path())
163 		return;
164 
165 	FOR_EACH_PTR(expr->args, arg) {
166 		if (is_initialized(arg))
167 			continue;
168 
169 		name = expr_to_str(arg);
170 		sm_warning("passing uninitialized '%s'", name);
171 		free_string(name);
172 
173 		set_state_expr(my_id, arg, &initialized);
174 	} END_FOR_EACH_PTR(arg);
175 }
176 
177 static int param_used_callback(void *found, int argc, char **argv, char **azColName)
178 {
179 	*(int *)found = 1;
180 	return 0;
181 }
182 
183 static int member_is_used(struct expression *call, int param, char *printed_name)
184 {
185 	int found;
186 
187 	/* for function pointers assume everything is used */
188 	if (call->fn->type != EXPR_SYMBOL)
189 		return 0;
190 
191 	found = 0;
192 	run_sql(&param_used_callback, &found,
193 		"select * from return_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;
196 }
197 
198 static void match_call_struct_members(struct expression *expr)
199 {
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;
206 
207 	return;
208 
209 	if (parse_error)
210 		return;
211 
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_warning("struct member %s is uninitialized", sm->name);
232 		} END_FOR_EACH_SM(sm);
233 
234 free:
235 		free_string(arg_name);
236 	} END_FOR_EACH_PTR(arg);
237 }
238 
239 static int is_being_modified(struct expression *expr)
240 {
241 	struct expression *parent;
242 	struct statement *stmt;
243 
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)
250 			return 0;
251 	}
252 	if (parent->type == EXPR_PREOP && parent->op == '&')
253 		return 1;
254 	if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr))
255 		return 1;
256 
257 	stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
258 	if (stmt && stmt->type == STMT_ASM)
259 		return 1;
260 
261 	return 0;
262 }
263 
264 static void match_symbol(struct expression *expr)
265 {
266 	char *name;
267 
268 	if (parse_error)
269 		return;
270 
271 	if (is_impossible_path())
272 		return;
273 
274 	if (is_initialized(expr))
275 		return;
276 
277 	if (is_being_modified(expr))
278 		return;
279 
280 	name = expr_to_str(expr);
281 	sm_error("uninitialized symbol '%s'.", name);
282 	free_string(name);
283 
284 	set_state_expr(my_id, expr, &initialized);
285 }
286 
287 static void match_untracked(struct expression *call, int param)
288 {
289 	struct expression *arg;
290 
291 	arg = get_argument_from_call_expr(call->args, param);
292 	arg = strip_expr(arg);
293 	if (!arg || arg->type != EXPR_PREOP || arg->op != '&')
294 		return;
295 	arg = strip_expr(arg->unop);
296 	set_state_expr(my_id, arg, &initialized);
297 }
298 
299 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr)
300 {
301 	int arg_nr = PTR_INT(_arg_nr);
302 	struct expression *arg;
303 
304 	arg = get_argument_from_call_expr(expr->args, arg_nr);
305 	arg = strip_expr(arg);
306 	if (!arg)
307 		return;
308 	if (arg->type != EXPR_PREOP || arg->op != '&')
309 		return;
310 	arg = strip_expr(arg->unop);
311 	set_state_expr(my_id, arg, &initialized);
312 }
313 
314 static void register_ignored_params_from_file(void)
315 {
316 	char name[256];
317 	struct token *token;
318 	const char *func;
319 	char prev_func[256];
320 	int param;
321 
322 	memset(prev_func, 0, sizeof(prev_func));
323 	snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str);
324 	name[255] = '\0';
325 	token = get_tokens_file(name);
326 	if (!token)
327 		return;
328 	if (token_type(token) != TOKEN_STREAMBEGIN)
329 		return;
330 	token = token->next;
331 	while (token_type(token) != TOKEN_STREAMEND) {
332 		if (token_type(token) != TOKEN_IDENT)
333 			return;
334 		func = show_ident(token->ident);
335 
336 		token = token->next;
337 		if (token_type(token) != TOKEN_NUMBER)
338 			return;
339 		param = atoi(token->number);
340 
341 		add_function_hook(func, &match_ignore_param, INT_PTR(param));
342 
343 		token = token->next;
344 	}
345 	clear_token_alloc();
346 }
347 
348 void check_uninitialized(int id)
349 {
350 	my_id = id;
351 
352 	add_hook(&match_declarations, DECLARATION_HOOK);
353 	add_extra_mod_hook(&extra_mod_hook);
354 	add_hook(&match_assign, ASSIGNMENT_HOOK);
355 	add_untracked_param_hook(&match_untracked);
356 	add_pre_merge_hook(my_id, &pre_merge_hook);
357 
358 	add_hook(&match_dereferences, DEREF_HOOK);
359 	add_hook(&match_condition, CONDITION_HOOK);
360 	add_hook(&match_call, FUNCTION_CALL_HOOK);
361 	add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK);
362 	add_hook(&match_symbol, SYM_HOOK);
363 
364 	register_ignored_params_from_file();
365 }
366