xref: /illumos-gate/usr/src/tools/smatch/src/smatch_function_ptrs.c (revision efe51d0cc2398b9ac179568b63a44e4bf295b8e2)
1 /*
2  * Copyright (C) 2013 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  * Track how functions are saved as various struct members or passed as
20  * parameters.
21  *
22  */
23 
24 #include "scope.h"
25 #include "smatch.h"
26 #include "smatch_slist.h"
27 
28 static int my_id;
29 
30 static char *get_from__symbol_get(struct expression *expr)
31 {
32 	struct expression *arg;
33 
34 	/*
35 	 * typeof(&dib0070_attach) __a =
36 	 * ((((typeof(&dib0070_attach)) (__symbol_get("dib0070_attach")))) ?:
37 	 *  (__request_module(true, "symbol:" "dib0070_attach"), (((typeof(&dib0070_attach))(__symbol_get("dib0070_attach"))))));
38 	 */
39 
40 	expr = strip_expr(expr);
41 
42 	if (expr->type != EXPR_CALL)
43 		return NULL;
44 	if (!sym_name_is("__symbol_get", expr->fn))
45 		return NULL;
46 	arg = get_argument_from_call_expr(expr->args, 0);
47 	if (!arg || arg->type != EXPR_STRING)
48 		return NULL;
49 
50 	return alloc_string(arg->string->data);
51 }
52 
53 static char *get_array_ptr(struct expression *expr)
54 {
55 	struct expression *array;
56 	struct symbol *type;
57 	char *name;
58 	char buf[256];
59 
60 	array = get_array_base(expr);
61 
62 	if (array) {
63 		name = get_member_name(array);
64 		if (name)
65 			return name;
66 	}
67 
68 	/* FIXME:  is_array() should probably be is_array_element() */
69 	type = get_type(expr);
70 	if (!array && type && type->type == SYM_ARRAY)
71 		array = expr;
72 	if (array) {
73 		name = expr_to_var(array);
74 		if (!name)
75 			return NULL;
76 		snprintf(buf, sizeof(buf), "%s[]", name);
77 		return alloc_string(buf);
78 	}
79 
80 	expr = get_assigned_expr(expr);
81 	array = get_array_base(expr);
82 	if (!array)
83 		return NULL;
84 	name = expr_to_var(array);
85 	if (!name)
86 		return NULL;
87 	snprintf(buf, sizeof(buf), "%s[]", name);
88 	free_string(name);
89 	return alloc_string(buf);
90 }
91 
92 static int is_local_symbol(struct symbol *sym)
93 {
94 	if (!sym ||
95 	    !(sym->ctype.modifiers & MOD_TOPLEVEL))
96 		return 1;
97 	return 0;
98 }
99 
100 static char *ptr_prefix(struct symbol *sym)
101 {
102 	static char buf[128];
103 
104 
105 	if (is_local_symbol(sym))
106 		snprintf(buf, sizeof(buf), "%s ptr", get_function());
107 	else if (sym && toplevel(sym->scope))
108 		snprintf(buf, sizeof(buf), "%s ptr", get_base_file());
109 	else
110 		snprintf(buf, sizeof(buf), "ptr");
111 
112 	return buf;
113 }
114 
115 char *get_returned_ptr(struct expression *expr)
116 {
117 	struct symbol *type;
118 	char *name;
119 	char buf[256];
120 
121 	if (expr->type != EXPR_CALL)
122 		return NULL;
123 	if (!expr->fn || expr->fn->type != EXPR_SYMBOL)
124 		return NULL;
125 
126 	type = get_type(expr);
127 	if (type && type->type == SYM_PTR)
128 		type = get_real_base_type(type);
129 	if (!type || type->type != SYM_FN)
130 		return NULL;
131 
132 	name = expr_to_var(expr->fn);
133 	if (!name)
134 		return NULL;
135 	snprintf(buf, sizeof(buf), "r %s()", name);
136 	free_string(name);
137 	return alloc_string(buf);
138 }
139 
140 char *get_fnptr_name(struct expression *expr)
141 {
142 	char *name;
143 
144 	if (is_zero(expr))
145 		return NULL;
146 
147 	expr = strip_expr(expr);
148 
149 	/* (*ptrs[0])(a, b, c) is the same as ptrs[0](a, b, c); */
150 	if (expr->type == EXPR_PREOP && expr->op == '*')
151 		expr = strip_expr(expr->unop);
152 
153 	name = get_from__symbol_get(expr);
154 	if (name)
155 		return name;
156 
157 	name = get_array_ptr(expr);
158 	if (name)
159 		return name;
160 
161 	name = get_returned_ptr(expr);
162 	if (name)
163 		return name;
164 
165 	name = get_member_name(expr);
166 	if (name)
167 		return name;
168 
169 	if (expr->type == EXPR_SYMBOL) {
170 		int param;
171 		char buf[256];
172 		struct symbol *sym;
173 		struct symbol *type;
174 
175 		param = get_param_num_from_sym(expr->symbol);
176 		if (param >= 0) {
177 			snprintf(buf, sizeof(buf), "%s param %d", get_function(), param);
178 			return alloc_string(buf);
179 		}
180 
181 		name =  expr_to_var_sym(expr, &sym);
182 		if (!name)
183 			return NULL;
184 		type = get_type(expr);
185 		if (type && type->type == SYM_PTR) {
186 			snprintf(buf, sizeof(buf), "%s %s", ptr_prefix(sym), name);
187 			free_string(name);
188 			return alloc_string(buf);
189 		}
190 		return name;
191 	}
192 	return expr_to_var(expr);
193 }
194 
195 static void match_passes_function_pointer(struct expression *expr)
196 {
197 	struct expression *arg, *tmp;
198 	struct symbol *type;
199 	char *called_name;
200 	char *fn_name;
201 	char ptr_name[256];
202 	int i;
203 
204 
205 	i = -1;
206 	FOR_EACH_PTR(expr->args, arg) {
207 		i++;
208 
209 		tmp = strip_expr(arg);
210 		if (tmp->type == EXPR_PREOP && tmp->op == '&')
211 			tmp = strip_expr(tmp->unop);
212 
213 		type = get_type(tmp);
214 		if (type && type->type == SYM_PTR)
215 			type = get_real_base_type(type);
216 		if (!type || type->type != SYM_FN)
217 			continue;
218 
219 		called_name = expr_to_var(expr->fn);
220 		if (!called_name)
221 			return;
222 		fn_name = get_fnptr_name(tmp);
223 		if (!fn_name)
224 			goto free;
225 
226 		snprintf(ptr_name, sizeof(ptr_name), "%s param %d", called_name, i);
227 		sql_insert_function_ptr(fn_name, ptr_name);
228 free:
229 		free_string(fn_name);
230 		free_string(called_name);
231 	} END_FOR_EACH_PTR(arg);
232 
233 }
234 
235 static int get_row_count(void *_row_count, int argc, char **argv, char **azColName)
236 {
237 	int *row_count = _row_count;
238 
239 	*row_count = 0;
240 	if (argc != 1)
241 		return 0;
242 	*row_count = atoi(argv[0]);
243 	return 0;
244 }
245 
246 static int can_hold_function_ptr(struct expression *expr)
247 {
248 	struct symbol *type;
249 
250 	type = get_type(expr);
251 	if (!type)
252 		return 0;
253 	if (type->type == SYM_PTR || type->type == SYM_ARRAY) {
254 		type = get_real_base_type(type);
255 		if (!type)
256 			return 0;
257 	}
258 	if (type->type == SYM_FN)
259 		return 1;
260 	if (type == &ulong_ctype && expr->type == EXPR_DEREF)
261 		return 1;
262 	if (type == &void_ctype)
263 		return 1;
264 	return 0;
265 }
266 
267 static void match_function_assign(struct expression *expr)
268 {
269 	struct expression *right;
270 	struct symbol *type;
271 	char *fn_name;
272 	char *ptr_name;
273 
274 	if (__in_fake_assign)
275 		return;
276 
277 	right = strip_expr(expr->right);
278 	if (right->type == EXPR_PREOP && right->op == '&')
279 		right = strip_expr(right->unop);
280 
281 	if (right->type != EXPR_SYMBOL &&
282 	    right->type != EXPR_DEREF)
283 		return;
284 
285 	if (!can_hold_function_ptr(right) ||
286 	    !can_hold_function_ptr(expr->left))
287 		return;
288 
289 	fn_name = get_fnptr_name(right);
290 	ptr_name = get_fnptr_name(expr->left);
291 	if (!fn_name || !ptr_name)
292 		goto free;
293 	if (strcmp(fn_name, ptr_name) == 0)
294 		goto free;
295 
296 
297 	type = get_type(right);
298 	if (!type)
299 		return;
300 	if (type->type == SYM_PTR || type->type == SYM_ARRAY) {
301 		type = get_real_base_type(type);
302 		if (!type)
303 			return;
304 	}
305 	if (type->type != SYM_FN) {
306 		int count = 0;
307 
308 		/* look it up in function_ptr */
309 		run_sql(get_row_count, &count,
310 			"select count(*) from function_ptr where ptr = '%s'",
311 			fn_name);
312 		if (count == 0)
313 			goto free;
314 	}
315 
316 	sql_insert_function_ptr(fn_name, ptr_name);
317 free:
318 	free_string(fn_name);
319 	free_string(ptr_name);
320 }
321 
322 static void match_returns_function_pointer(struct expression *expr)
323 {
324 	struct symbol *type;
325 	char *fn_name;
326 	char ptr_name[256];
327 
328 	if (__inline_fn)
329 		return;
330 
331 	type = get_real_base_type(cur_func_sym);
332 	if (!type || type->type != SYM_FN)
333 		return;
334 	type = get_real_base_type(type);
335 	if (!type || type->type != SYM_PTR)
336 		return;
337 	type = get_real_base_type(type);
338 	if (!type || type->type != SYM_FN)
339 		return;
340 
341 	if (expr->type == EXPR_PREOP && expr->op == '&')
342 		expr = strip_expr(expr->unop);
343 
344 	fn_name = get_fnptr_name(expr);
345 	if (!fn_name)
346 		return;
347 	snprintf(ptr_name, sizeof(ptr_name), "r %s()", get_function());
348 	sql_insert_function_ptr(fn_name, ptr_name);
349 }
350 
351 static void print_initializer_list(struct expression_list *expr_list,
352 		struct symbol *struct_type)
353 {
354 	struct expression *expr;
355 	struct symbol *base_type;
356 	char struct_name[256];
357 
358 	FOR_EACH_PTR(expr_list, expr) {
359 		if (expr->type == EXPR_INDEX && expr->idx_expression && expr->idx_expression->type == EXPR_INITIALIZER) {
360 			print_initializer_list(expr->idx_expression->expr_list, struct_type);
361 			continue;
362 		}
363 		if (expr->type != EXPR_IDENTIFIER)
364 			continue;
365 		if (!expr->expr_ident)
366 			continue;
367 		if (!expr->ident_expression ||
368 		    expr->ident_expression->type != EXPR_SYMBOL ||
369 		    !expr->ident_expression->symbol_name)
370 			continue;
371 		base_type = get_type(expr->ident_expression);
372 		if (!base_type || base_type->type != SYM_FN)
373 			continue;
374 		snprintf(struct_name, sizeof(struct_name), "(struct %s)->%s",
375 			 struct_type->ident->name, expr->expr_ident->name);
376 		sql_insert_function_ptr(expr->ident_expression->symbol_name->name,
377 				        struct_name);
378 	} END_FOR_EACH_PTR(expr);
379 }
380 
381 static void global_variable(struct symbol *sym)
382 {
383 	struct symbol *struct_type;
384 
385 	if (!sym->ident)
386 		return;
387 	if (!sym->initializer || sym->initializer->type != EXPR_INITIALIZER)
388 		return;
389 	struct_type = get_base_type(sym);
390 	if (!struct_type)
391 		return;
392 	if (struct_type->type == SYM_ARRAY) {
393 		struct_type = get_base_type(struct_type);
394 		if (!struct_type)
395 			return;
396 	}
397 	if (struct_type->type != SYM_STRUCT || !struct_type->ident)
398 		return;
399 	print_initializer_list(sym->initializer->expr_list, struct_type);
400 }
401 
402 void register_function_ptrs(int id)
403 {
404 	my_id = id;
405 
406 	if (!option_info)
407 		return;
408 
409 	add_hook(&global_variable, BASE_HOOK);
410 	add_hook(&global_variable, DECLARATION_HOOK);
411 	add_hook(&match_passes_function_pointer, FUNCTION_CALL_HOOK);
412 	add_hook(&match_returns_function_pointer, RETURN_HOOK);
413 	add_hook(&match_function_assign, ASSIGNMENT_HOOK);
414 	add_hook(&match_function_assign, GLOBAL_ASSIGNMENT_HOOK);
415 }
416