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