xref: /illumos-gate/usr/src/tools/smatch/src/smatch_untracked_param.c (revision 2a1fd0ffe121888d44fdec321c25b53dcfaa9118)
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 
43 typedef void (untracked_hook)(struct expression *call, int param);
44 DECLARE_PTR_LIST(untracked_hook_list, untracked_hook *);
45 static struct untracked_hook_list *untracked_hooks;
46 
47 struct int_stack *tracked_stack;
48 
49 void add_untracked_param_hook(void (func)(struct expression *call, int param))
50 {
51 	untracked_hook **p = malloc(sizeof(untracked_hook *));
52 	*p = func;
53 	add_ptr_list(&untracked_hooks, p);
54 }
55 
56 static void call_untracked_callbacks(struct expression *expr, int param)
57 {
58 	untracked_hook **fn;
59 
60 	FOR_EACH_PTR(untracked_hooks, fn) {
61 		(*fn)(expr, param);
62 	} END_FOR_EACH_PTR(fn);
63 }
64 
65 static void assume_tracked(struct expression *call_expr, int param, char *key, char *value)
66 {
67 	tracked = 1;
68 }
69 
70 void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
71 {
72 	char *name;
73 	struct symbol *sym;
74 
75 	while (expr->type == EXPR_ASSIGNMENT)
76 		expr = strip_expr(expr->right);
77 	if (expr->type != EXPR_CALL)
78 		return;
79 
80 	name = return_state_to_var_sym(expr, param, key, &sym);
81 	if (!name || !sym)
82 		goto free;
83 
84 	call_untracked_callbacks(expr, param);
85 	set_state(my_id, name, sym, &untracked);
86 free:
87 	free_string(name);
88 }
89 
90 static int lost_in_va_args(struct expression *expr)
91 {
92 	struct symbol *fn;
93 	char *name;
94 	int is_lost;
95 
96 	fn = get_type(expr->fn);
97 	if (!fn || !fn->variadic)
98 		return 0;
99 
100 	is_lost = 1;
101 	name = expr_to_var(expr->fn);
102 	if (name && strstr(name, "print"))
103 		is_lost = 0;
104 	free_string(name);
105 
106 	return is_lost;
107 }
108 
109 static void match_after_call(struct expression *expr)
110 {
111 	struct expression *arg;
112 	struct symbol *type;
113 	int i;
114 
115 	if (lost_in_va_args(expr))
116 		tracked = 0;
117 
118 	if (tracked) {
119 		tracked = 0;
120 		return;
121 	}
122 
123 	i = -1;
124 	FOR_EACH_PTR(expr->args, arg) {
125 		i++;
126 
127 		type = get_type(arg);
128 		if (!type || type->type != SYM_PTR)
129 			continue;
130 
131 		call_untracked_callbacks(expr, i);
132 		set_state_expr(my_id, arg, &untracked);
133 	} END_FOR_EACH_PTR(arg);
134 }
135 
136 void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
137 {
138 	struct symbol *arg;
139 	int param;
140 
141 	param = -1;
142 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
143 		param++;
144 
145 		if (!arg->ident)
146 			continue;
147 		sql_insert_return_states(return_id, return_ranges,
148 					 UNTRACKED_PARAM, param, "$", "");
149 	} END_FOR_EACH_PTR(arg);
150 }
151 
152 static void print_untracked_params(int return_id, char *return_ranges, struct expression *expr)
153 {
154 	struct symbol *arg;
155 	int param;
156 
157 	param = -1;
158 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
159 		param++;
160 
161 		if (!arg->ident)
162 			continue;
163 		if (!get_state(my_id, arg->ident->name, arg) &&
164 		    !__bail_on_rest_of_function)  /* hairy functions are untrackable */
165 			continue;
166 
167 		sql_insert_return_states(return_id, return_ranges,
168 					 UNTRACKED_PARAM, param, "$", "");
169 	} END_FOR_EACH_PTR(arg);
170 }
171 
172 static void match_param_assign(struct expression *expr)
173 {
174 	struct expression *right;
175 	struct symbol *type;
176 	int param;
177 
178 	if (__in_fake_assign)
179 		return;
180 
181 	right = strip_expr(expr->right);
182 	type = get_type(right);
183 	if (!type || type->type != SYM_PTR)
184 		return;
185 
186 	param = get_param_num(right);
187 	if (param < 0)
188 		return;
189 
190 	set_state_expr(my_id, right, &untracked);
191 }
192 
193 
194 static void match_param_assign_in_asm(struct statement *stmt)
195 {
196 
197 	struct expression *expr;
198 	struct symbol *type;
199 	int state = 0;
200 	int param;
201 
202 	FOR_EACH_PTR(stmt->asm_inputs, expr) {
203 		switch (state) {
204 		case 0: /* identifier */
205 		case 1: /* constraint */
206 			state++;
207 			continue;
208 		case 2: /* expression */
209 			state = 0;
210 
211 			expr = strip_expr(expr);
212 			type = get_type(expr);
213 			if (!type || type->type != SYM_PTR)
214 				continue;
215 			param = get_param_num(expr);
216 			if (param < 0)
217 				continue;
218 			set_state_expr(my_id, expr, &untracked);
219 			continue;
220 		}
221 	} END_FOR_EACH_PTR(expr);
222 }
223 
224 static void match_inline_start(struct expression *expr)
225 {
226 	push_int(&tracked_stack, tracked);
227 }
228 
229 static void match_inline_end(struct expression *expr)
230 {
231 	tracked = pop_int(&tracked_stack);
232 }
233 
234 void register_untracked_param(int id)
235 {
236 	my_id = id;
237 
238 	select_return_states_hook(INTERNAL, &assume_tracked);
239 	select_return_states_hook(UNTRACKED_PARAM, &mark_untracked);
240 	add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB);
241 
242 	add_split_return_callback(&print_untracked_params);
243 
244 	add_hook(&match_param_assign, ASSIGNMENT_HOOK);
245 	add_hook(&match_param_assign_in_asm, ASM_HOOK);
246 
247 	add_hook(&match_inline_start, INLINE_FN_START);
248 	add_hook(&match_inline_end, INLINE_FN_END);
249 }
250