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 
pre_merge_hook(struct sm_state * cur,struct sm_state * other)27 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
28 {
29 	if (is_impossible_path())
30 		set_state(my_id, cur->name, cur->sym, &initialized);
31 }
32 
mark_members_uninitialized(struct symbol * sym)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 
match_declarations(struct symbol * sym)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 
extra_mod_hook(const char * name,struct symbol * sym,struct expression * expr,struct smatch_state * state)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 
match_assign(struct expression * expr)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 
match_negative_comparison(struct expression * expr)98 static void match_negative_comparison(struct expression *expr)
99 {
100 	struct expression *success;
101 	struct sm_state *sm;
102 	sval_t max;
103 
104 	/*
105 	 * In the kernel, people don't use "if (ret) {" and "if (ret < 0) {"
106 	 * consistently.  Ideally Smatch would know the return but often it
107 	 * doesn't.
108 	 *
109 	 */
110 
111 	if (option_project != PROJ_KERNEL)
112 		return;
113 
114 	if (expr->type != EXPR_COMPARE || expr->op != '<')
115 		return;
116 	if (!expr_is_zero(expr->right))
117 		return;
118 	if (get_implied_max(expr->left, &max) && max.value == 0)
119 		return;
120 
121 	success = compare_expression(expr->left, SPECIAL_EQUAL, expr->right);
122 	if (!assume(success))
123 		return;
124 
125 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
126 		if (sm->state == &initialized)
127 			set_true_false_states(my_id, sm->name, sm->sym, NULL, &initialized);
128 	} END_FOR_EACH_SM(sm);
129 
130 	end_assume();
131 }
132 
is_initialized(struct expression * expr)133 static int is_initialized(struct expression *expr)
134 {
135 	struct sm_state *sm;
136 
137 	expr = strip_expr(expr);
138 	if (expr->type != EXPR_SYMBOL)
139 		return 1;
140 	sm = get_sm_state_expr(my_id, expr);
141 	if (!sm)
142 		return 1;
143 	if (!slist_has_state(sm->possible, &uninitialized))
144 		return 1;
145 	return 0;
146 }
147 
match_dereferences(struct expression * expr)148 static void match_dereferences(struct expression *expr)
149 {
150 	char *name;
151 
152 	if (implications_off || parse_error)
153 		return;
154 
155 	if (expr->type != EXPR_PREOP)
156 		return;
157 	if (is_impossible_path())
158 		return;
159 	if (is_initialized(expr->unop))
160 		return;
161 
162 	name = expr_to_str(expr->unop);
163 	sm_error("potentially dereferencing uninitialized '%s'.", name);
164 	free_string(name);
165 
166 	set_state_expr(my_id, expr->unop, &initialized);
167 }
168 
match_condition(struct expression * expr)169 static void match_condition(struct expression *expr)
170 {
171 	char *name;
172 
173 	if (implications_off || parse_error)
174 		return;
175 
176 	if (is_impossible_path())
177 		return;
178 
179 	if (is_initialized(expr))
180 		return;
181 
182 	name = expr_to_str(expr);
183 	sm_error("potentially using uninitialized '%s'.", name);
184 	free_string(name);
185 
186 	set_state_expr(my_id, expr, &initialized);
187 }
188 
match_call(struct expression * expr)189 static void match_call(struct expression *expr)
190 {
191 	struct expression *arg;
192 	char *name;
193 
194 	if (parse_error)
195 		return;
196 
197 	if (is_impossible_path())
198 		return;
199 
200 	FOR_EACH_PTR(expr->args, arg) {
201 		if (is_initialized(arg))
202 			continue;
203 
204 		name = expr_to_str(arg);
205 		sm_warning("passing uninitialized '%s'", name);
206 		free_string(name);
207 
208 		set_state_expr(my_id, arg, &initialized);
209 	} END_FOR_EACH_PTR(arg);
210 }
211 
param_used_callback(void * found,int argc,char ** argv,char ** azColName)212 static int param_used_callback(void *found, int argc, char **argv, char **azColName)
213 {
214 	*(int *)found = 1;
215 	return 0;
216 }
217 
member_is_used(struct expression * call,int param,char * printed_name)218 static int member_is_used(struct expression *call, int param, char *printed_name)
219 {
220 	int found;
221 
222 	/* for function pointers assume everything is used */
223 	if (call->fn->type != EXPR_SYMBOL)
224 		return 0;
225 
226 	found = 0;
227 	run_sql(&param_used_callback, &found,
228 		"select * from return_implies where %s and type = %d and parameter = %d and key = '%s';",
229 		get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name);
230 	return found;
231 }
232 
match_call_struct_members(struct expression * expr)233 static void match_call_struct_members(struct expression *expr)
234 {
235 	struct symbol *type, *sym;
236 	struct expression *arg;
237 	struct sm_state *sm;
238 	char *arg_name;
239 	char buf[256];
240 	int param;
241 
242 	return;
243 
244 	if (parse_error)
245 		return;
246 
247 	param = -1;
248 	FOR_EACH_PTR(expr->args, arg) {
249 		param++;
250 		if (arg->type != EXPR_PREOP || arg->op != '&')
251 			continue;
252 		type = get_type(arg->unop);
253 		if (!type || type->type != SYM_STRUCT)
254 			continue;
255 		arg_name = expr_to_var_sym(arg->unop, &sym);
256 		if (!arg_name || !sym)
257 			goto free;
258 		FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
259 			if (sm->sym != sym)
260 				continue;
261 			if (!slist_has_state(sm->possible, &uninitialized))
262 				continue;
263 			snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1);
264 			if (!member_is_used(expr, param, buf))
265 				goto free;
266 			sm_warning("struct member %s is uninitialized", sm->name);
267 		} END_FOR_EACH_SM(sm);
268 
269 free:
270 		free_string(arg_name);
271 	} END_FOR_EACH_PTR(arg);
272 }
273 
is_being_modified(struct expression * expr)274 static int is_being_modified(struct expression *expr)
275 {
276 	struct expression *parent;
277 	struct statement *stmt;
278 
279 	parent = expr_get_parent_expr(expr);
280 	if (!parent)
281 		return 0;
282 	while (parent->type == EXPR_PREOP && parent->op == '(') {
283 		parent = expr_get_parent_expr(parent);
284 		if (!parent)
285 			return 0;
286 	}
287 	if (parent->type == EXPR_PREOP && parent->op == '&')
288 		return 1;
289 	if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr))
290 		return 1;
291 
292 	stmt = last_ptr_list((struct ptr_list *)big_statement_stack);
293 	if (stmt && stmt->type == STMT_ASM)
294 		return 1;
295 
296 	return 0;
297 }
298 
match_symbol(struct expression * expr)299 static void match_symbol(struct expression *expr)
300 {
301 	char *name;
302 
303 	if (implications_off || parse_error)
304 		return;
305 
306 	if (is_impossible_path())
307 		return;
308 
309 	if (is_initialized(expr))
310 		return;
311 
312 	if (is_being_modified(expr))
313 		return;
314 
315 	name = expr_to_str(expr);
316 	sm_error("uninitialized symbol '%s'.", name);
317 	free_string(name);
318 
319 	set_state_expr(my_id, expr, &initialized);
320 }
321 
match_untracked(struct expression * call,int param)322 static void match_untracked(struct expression *call, int param)
323 {
324 	struct expression *arg;
325 
326 	arg = get_argument_from_call_expr(call->args, param);
327 	arg = strip_expr(arg);
328 	if (!arg || arg->type != EXPR_PREOP || arg->op != '&')
329 		return;
330 	arg = strip_expr(arg->unop);
331 	set_state_expr(my_id, arg, &initialized);
332 }
333 
match_ignore_param(const char * fn,struct expression * expr,void * _arg_nr)334 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr)
335 {
336 	int arg_nr = PTR_INT(_arg_nr);
337 	struct expression *arg;
338 
339 	arg = get_argument_from_call_expr(expr->args, arg_nr);
340 	arg = strip_expr(arg);
341 	if (!arg)
342 		return;
343 	if (arg->type != EXPR_PREOP || arg->op != '&')
344 		return;
345 	arg = strip_expr(arg->unop);
346 	set_state_expr(my_id, arg, &initialized);
347 }
348 
register_ignored_params_from_file(void)349 static void register_ignored_params_from_file(void)
350 {
351 	char name[256];
352 	struct token *token;
353 	const char *func;
354 	char prev_func[256];
355 	int param;
356 
357 	memset(prev_func, 0, sizeof(prev_func));
358 	snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str);
359 	name[255] = '\0';
360 	token = get_tokens_file(name);
361 	if (!token)
362 		return;
363 	if (token_type(token) != TOKEN_STREAMBEGIN)
364 		return;
365 	token = token->next;
366 	while (token_type(token) != TOKEN_STREAMEND) {
367 		if (token_type(token) != TOKEN_IDENT)
368 			return;
369 		func = show_ident(token->ident);
370 
371 		token = token->next;
372 		if (token_type(token) != TOKEN_NUMBER)
373 			return;
374 		param = atoi(token->number);
375 
376 		add_function_hook(func, &match_ignore_param, INT_PTR(param));
377 
378 		token = token->next;
379 	}
380 	clear_token_alloc();
381 }
382 
check_uninitialized(int id)383 void check_uninitialized(int id)
384 {
385 	my_id = id;
386 
387 	add_hook(&match_declarations, DECLARATION_HOOK);
388 	add_extra_mod_hook(&extra_mod_hook);
389 	add_hook(&match_assign, ASSIGNMENT_HOOK);
390 	add_hook(&match_negative_comparison, CONDITION_HOOK);
391 	add_untracked_param_hook(&match_untracked);
392 	add_pre_merge_hook(my_id, &pre_merge_hook);
393 
394 	add_hook(&match_dereferences, DEREF_HOOK);
395 	add_hook(&match_condition, CONDITION_HOOK);
396 	add_hook(&match_call, FUNCTION_CALL_HOOK);
397 	add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK);
398 	add_hook(&match_symbol, SYM_HOOK);
399 
400 	register_ignored_params_from_file();
401 }
402