xref: /illumos-gate/usr/src/tools/smatch/src/smatch_untracked_param.c (revision d8109ce4330e1b8ad6c29f9fccacec969066bb9d)
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 /*
19  * Sometimes we aren't able to track a variable through a function call.  This
20  * usually happens because a function changes too many variables so we give up.
21  * Another reason this happens is because we call a function pointer and there
22  * are too many functions which implement that function pointer so we give up.
23  * Also maybe we don't have the database enabled.
24  *
25  * The goal here is to make a call back so what if we call:
26  *
27  * 	frob(&foo);
28  *
29  * but we're not able to say what happens to "foo", then let's assume that we
30  * don't know anything about "foo" if it's an untracked call.
31  *
32  */
33 
34 #include "smatch.h"
35 #include "smatch_slist.h"
36 #include "smatch_extra.h"
37 
38 static int my_id;
39 static int tracked;
40 
41 STATE(untracked);
42 STATE(lost);
43 
44 typedef void (untracked_hook)(struct expression *call, int param);
45 DECLARE_PTR_LIST(untracked_hook_list, untracked_hook *);
46 static struct untracked_hook_list *untracked_hooks;
47 static struct untracked_hook_list *lost_hooks;
48 
49 struct int_stack *tracked_stack;
50 
51 void add_untracked_param_hook(void (func)(struct expression *call, int param))
52 {
53 	untracked_hook **p = malloc(sizeof(untracked_hook *));
54 	*p = func;
55 	add_ptr_list(&untracked_hooks, p);
56 }
57 
58 static void call_untracked_callbacks(struct expression *expr, int param)
59 {
60 	untracked_hook **fn;
61 
62 	FOR_EACH_PTR(untracked_hooks, fn) {
63 		(*fn)(expr, param);
64 	} END_FOR_EACH_PTR(fn);
65 }
66 
67 void add_lost_param_hook(void (func)(struct expression *call, int param))
68 {
69 	untracked_hook **p = malloc(sizeof(untracked_hook *));
70 	*p = func;
71 	add_ptr_list(&lost_hooks, p);
72 }
73 
74 static void call_lost_callbacks(struct expression *expr, int param)
75 {
76 	untracked_hook **fn;
77 
78 	FOR_EACH_PTR(lost_hooks, fn) {
79 		(*fn)(expr, param);
80 	} END_FOR_EACH_PTR(fn);
81 }
82 
83 static void assume_tracked(struct expression *call_expr, int param, char *key, char *value)
84 {
85 	tracked = 1;
86 }
87 
88 static char *get_array_from_key(struct expression *expr, int param, const char *key, struct symbol **sym)
89 {
90 	struct expression *arg;
91 
92 	arg = get_argument_from_call_expr(expr->args, param);
93 	if (!arg)
94 		return NULL;
95 	if (arg->type != EXPR_PREOP || arg->op != '&')
96 		return NULL;
97 	arg = arg->unop;
98 	if (!is_array(arg))
99 		return NULL;
100 	arg = get_array_base(arg);
101 
102 	return expr_to_var_sym(arg, sym);
103 }
104 
105 static void mark_untracked_lost(struct expression *expr, int param, const char *key, int type)
106 {
107 	char *name;
108 	struct symbol *sym;
109 
110 	while (expr->type == EXPR_ASSIGNMENT)
111 		expr = strip_expr(expr->right);
112 	if (expr->type != EXPR_CALL)
113 		return;
114 
115 	name = return_state_to_var_sym(expr, param, key, &sym);
116 	if (!name || !sym) {
117 		name = get_array_from_key(expr, param, key, &sym);
118 		if (!name || !sym)
119 			goto free;
120 	}
121 
122 	if (type == LOST_PARAM)
123 		call_lost_callbacks(expr, param);
124 	call_untracked_callbacks(expr, param);
125 	set_state(my_id, name, sym, &untracked);
126 free:
127 	free_string(name);
128 
129 }
130 
131 void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
132 {
133 	mark_untracked_lost(expr, param, key, UNTRACKED_PARAM);
134 }
135 
136 void mark_lost(struct expression *expr, int param, const char *key, const char *value)
137 {
138 	mark_untracked_lost(expr, param, key, LOST_PARAM);
139 }
140 
141 static int lost_in_va_args(struct expression *expr)
142 {
143 	struct symbol *fn;
144 	char *name;
145 	int is_lost;
146 
147 	fn = get_type(expr->fn);
148 	if (!fn || !fn->variadic)
149 		return 0;
150 
151 	is_lost = 1;
152 	name = expr_to_var(expr->fn);
153 	if (name && strstr(name, "print"))
154 		is_lost = 0;
155 	free_string(name);
156 
157 	return is_lost;
158 }
159 
160 static void match_after_call(struct expression *expr)
161 {
162 	struct expression *arg;
163 	struct symbol *type;
164 	int i;
165 
166 	if (lost_in_va_args(expr))
167 		tracked = 0;
168 
169 	if (tracked) {
170 		tracked = 0;
171 		return;
172 	}
173 
174 	i = -1;
175 	FOR_EACH_PTR(expr->args, arg) {
176 		i++;
177 
178 		type = get_type(arg);
179 		if (!type || type->type != SYM_PTR)
180 			continue;
181 
182 		call_untracked_callbacks(expr, i);
183 		set_state_expr(my_id, arg, &untracked);
184 	} END_FOR_EACH_PTR(arg);
185 }
186 
187 
188 static void mark_all_params(int return_id, char *return_ranges, int type)
189 {
190 	struct symbol *arg;
191 	int param;
192 
193 	param = -1;
194 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
195 		param++;
196 
197 		if (!arg->ident)
198 			continue;
199 		sql_insert_return_states(return_id, return_ranges,
200 					 type, param, "$", "");
201 	} END_FOR_EACH_PTR(arg);
202 }
203 
204 
205 void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
206 {
207 	mark_all_params(return_id, return_ranges, UNTRACKED_PARAM);
208 }
209 
210 void mark_all_params_lost(int return_id, char *return_ranges, struct expression *expr)
211 {
212 	mark_all_params(return_id, return_ranges, LOST_PARAM);
213 }
214 
215 static void print_untracked_params(int return_id, char *return_ranges, struct expression *expr)
216 {
217 	struct sm_state *sm;
218 	struct symbol *arg;
219 	int param;
220 	int type;
221 
222 	param = -1;
223 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
224 		param++;
225 
226 		if (!arg->ident)
227 			continue;
228 
229 		if (__bail_on_rest_of_function) {
230 			/* hairy functions are lost */
231 			type = LOST_PARAM;
232 		} else if ((sm = get_sm_state(my_id, arg->ident->name, arg))) {
233 			if (slist_has_state(sm->possible, &lost))
234 				type = LOST_PARAM;
235 			else
236 				type = UNTRACKED_PARAM;
237 		} else {
238 			continue;
239 		}
240 
241 		sql_insert_return_states(return_id, return_ranges,
242 					 type, param, "$", "");
243 	} END_FOR_EACH_PTR(arg);
244 }
245 
246 static void match_param_assign(struct expression *expr)
247 {
248 	struct expression *right;
249 	struct symbol *type;
250 	int param;
251 
252 	if (__in_fake_assign)
253 		return;
254 
255 	right = strip_expr(expr->right);
256 	type = get_type(right);
257 	if (!type || type->type != SYM_PTR)
258 		return;
259 
260 	param = get_param_num(right);
261 	if (param < 0)
262 		return;
263 
264 	set_state_expr(my_id, right, &untracked);
265 }
266 
267 
268 static void match_param_assign_in_asm(struct statement *stmt)
269 {
270 
271 	struct expression *expr;
272 	struct symbol *type;
273 	int state = 0;
274 	int param;
275 
276 	FOR_EACH_PTR(stmt->asm_inputs, expr) {
277 		switch (state) {
278 		case 0: /* identifier */
279 		case 1: /* constraint */
280 			state++;
281 			continue;
282 		case 2: /* expression */
283 			state = 0;
284 
285 			expr = strip_expr(expr);
286 			type = get_type(expr);
287 			if (!type || type->type != SYM_PTR)
288 				continue;
289 			param = get_param_num(expr);
290 			if (param < 0)
291 				continue;
292 			set_state_expr(my_id, expr, &untracked);
293 			continue;
294 		}
295 	} END_FOR_EACH_PTR(expr);
296 }
297 
298 static void match_inline_start(struct expression *expr)
299 {
300 	push_int(&tracked_stack, tracked);
301 }
302 
303 static void match_inline_end(struct expression *expr)
304 {
305 	tracked = pop_int(&tracked_stack);
306 }
307 
308 void register_untracked_param(int id)
309 {
310 	my_id = id;
311 
312 	select_return_states_hook(INTERNAL, &assume_tracked);
313 	select_return_states_hook(UNTRACKED_PARAM, &mark_untracked);
314 	select_return_states_hook(LOST_PARAM, &mark_lost);
315 	add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB);
316 
317 	add_split_return_callback(&print_untracked_params);
318 
319 	add_hook(&match_param_assign, ASSIGNMENT_HOOK);
320 	add_hook(&match_param_assign_in_asm, ASM_HOOK);
321 
322 	add_hook(&match_inline_start, INLINE_FN_START);
323 	add_hook(&match_inline_end, INLINE_FN_END);
324 }
325