11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2014 Oracle.
31f5207b7SJohn Levon  *
41f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon  * of the License, or (at your option) any later version.
81f5207b7SJohn Levon  *
91f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
121f5207b7SJohn Levon  * GNU General Public License for more details.
131f5207b7SJohn Levon  *
141f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon  */
171f5207b7SJohn Levon 
181f5207b7SJohn Levon /*
191f5207b7SJohn Levon  * Sometimes we aren't able to track a variable through a function call.  This
201f5207b7SJohn Levon  * usually happens because a function changes too many variables so we give up.
211f5207b7SJohn Levon  * Another reason this happens is because we call a function pointer and there
221f5207b7SJohn Levon  * are too many functions which implement that function pointer so we give up.
231f5207b7SJohn Levon  * Also maybe we don't have the database enabled.
241f5207b7SJohn Levon  *
251f5207b7SJohn Levon  * The goal here is to make a call back so what if we call:
261f5207b7SJohn Levon  *
271f5207b7SJohn Levon  * 	frob(&foo);
281f5207b7SJohn Levon  *
291f5207b7SJohn Levon  * but we're not able to say what happens to "foo", then let's assume that we
301f5207b7SJohn Levon  * don't know anything about "foo" if it's an untracked call.
311f5207b7SJohn Levon  *
321f5207b7SJohn Levon  */
331f5207b7SJohn Levon 
341f5207b7SJohn Levon #include "smatch.h"
351f5207b7SJohn Levon #include "smatch_slist.h"
361f5207b7SJohn Levon #include "smatch_extra.h"
371f5207b7SJohn Levon 
381f5207b7SJohn Levon static int my_id;
391f5207b7SJohn Levon static int tracked;
401f5207b7SJohn Levon 
411f5207b7SJohn Levon STATE(untracked);
42efe51d0cSJohn Levon STATE(lost);
431f5207b7SJohn Levon 
441f5207b7SJohn Levon typedef void (untracked_hook)(struct expression *call, int param);
451f5207b7SJohn Levon DECLARE_PTR_LIST(untracked_hook_list, untracked_hook *);
461f5207b7SJohn Levon static struct untracked_hook_list *untracked_hooks;
47efe51d0cSJohn Levon static struct untracked_hook_list *lost_hooks;
481f5207b7SJohn Levon 
491f5207b7SJohn Levon struct int_stack *tracked_stack;
501f5207b7SJohn Levon 
add_untracked_param_hook(void (func)(struct expression * call,int param))511f5207b7SJohn Levon void add_untracked_param_hook(void (func)(struct expression *call, int param))
521f5207b7SJohn Levon {
531f5207b7SJohn Levon 	untracked_hook **p = malloc(sizeof(untracked_hook *));
541f5207b7SJohn Levon 	*p = func;
551f5207b7SJohn Levon 	add_ptr_list(&untracked_hooks, p);
561f5207b7SJohn Levon }
571f5207b7SJohn Levon 
call_untracked_callbacks(struct expression * expr,int param)581f5207b7SJohn Levon static void call_untracked_callbacks(struct expression *expr, int param)
591f5207b7SJohn Levon {
601f5207b7SJohn Levon 	untracked_hook **fn;
611f5207b7SJohn Levon 
621f5207b7SJohn Levon 	FOR_EACH_PTR(untracked_hooks, fn) {
631f5207b7SJohn Levon 		(*fn)(expr, param);
641f5207b7SJohn Levon 	} END_FOR_EACH_PTR(fn);
651f5207b7SJohn Levon }
661f5207b7SJohn Levon 
add_lost_param_hook(void (func)(struct expression * call,int param))67efe51d0cSJohn Levon void add_lost_param_hook(void (func)(struct expression *call, int param))
68efe51d0cSJohn Levon {
69efe51d0cSJohn Levon 	untracked_hook **p = malloc(sizeof(untracked_hook *));
70efe51d0cSJohn Levon 	*p = func;
71efe51d0cSJohn Levon 	add_ptr_list(&lost_hooks, p);
72efe51d0cSJohn Levon }
73efe51d0cSJohn Levon 
call_lost_callbacks(struct expression * expr,int param)74efe51d0cSJohn Levon static void call_lost_callbacks(struct expression *expr, int param)
75efe51d0cSJohn Levon {
76efe51d0cSJohn Levon 	untracked_hook **fn;
77efe51d0cSJohn Levon 
78efe51d0cSJohn Levon 	FOR_EACH_PTR(lost_hooks, fn) {
79efe51d0cSJohn Levon 		(*fn)(expr, param);
80efe51d0cSJohn Levon 	} END_FOR_EACH_PTR(fn);
81efe51d0cSJohn Levon }
82efe51d0cSJohn Levon 
assume_tracked(struct expression * call_expr,int param,char * key,char * value)831f5207b7SJohn Levon static void assume_tracked(struct expression *call_expr, int param, char *key, char *value)
841f5207b7SJohn Levon {
851f5207b7SJohn Levon 	tracked = 1;
861f5207b7SJohn Levon }
871f5207b7SJohn Levon 
get_array_from_key(struct expression * expr,int param,const char * key,struct symbol ** sym)88efe51d0cSJohn Levon static char *get_array_from_key(struct expression *expr, int param, const char *key, struct symbol **sym)
89efe51d0cSJohn Levon {
90efe51d0cSJohn Levon 	struct expression *arg;
91efe51d0cSJohn Levon 
92efe51d0cSJohn Levon 	arg = get_argument_from_call_expr(expr->args, param);
93efe51d0cSJohn Levon 	if (!arg)
94efe51d0cSJohn Levon 		return NULL;
95efe51d0cSJohn Levon 	if (arg->type != EXPR_PREOP || arg->op != '&')
96efe51d0cSJohn Levon 		return NULL;
97efe51d0cSJohn Levon 	arg = arg->unop;
98efe51d0cSJohn Levon 	if (!is_array(arg))
99efe51d0cSJohn Levon 		return NULL;
100efe51d0cSJohn Levon 	arg = get_array_base(arg);
101efe51d0cSJohn Levon 
102efe51d0cSJohn Levon 	return expr_to_var_sym(arg, sym);
103efe51d0cSJohn Levon }
104efe51d0cSJohn Levon 
mark_untracked_lost(struct expression * expr,int param,const char * key,int type)105efe51d0cSJohn Levon static void mark_untracked_lost(struct expression *expr, int param, const char *key, int type)
1061f5207b7SJohn Levon {
1071f5207b7SJohn Levon 	char *name;
1081f5207b7SJohn Levon 	struct symbol *sym;
1091f5207b7SJohn Levon 
1101f5207b7SJohn Levon 	while (expr->type == EXPR_ASSIGNMENT)
1111f5207b7SJohn Levon 		expr = strip_expr(expr->right);
1121f5207b7SJohn Levon 	if (expr->type != EXPR_CALL)
1131f5207b7SJohn Levon 		return;
1141f5207b7SJohn Levon 
1151f5207b7SJohn Levon 	name = return_state_to_var_sym(expr, param, key, &sym);
116efe51d0cSJohn Levon 	if (!name || !sym) {
117efe51d0cSJohn Levon 		name = get_array_from_key(expr, param, key, &sym);
118efe51d0cSJohn Levon 		if (!name || !sym)
119efe51d0cSJohn Levon 			goto free;
120efe51d0cSJohn Levon 	}
1211f5207b7SJohn Levon 
122efe51d0cSJohn Levon 	if (type == LOST_PARAM)
123efe51d0cSJohn Levon 		call_lost_callbacks(expr, param);
1241f5207b7SJohn Levon 	call_untracked_callbacks(expr, param);
1251f5207b7SJohn Levon 	set_state(my_id, name, sym, &untracked);
1261f5207b7SJohn Levon free:
1271f5207b7SJohn Levon 	free_string(name);
128efe51d0cSJohn Levon 
129efe51d0cSJohn Levon }
130efe51d0cSJohn Levon 
mark_untracked(struct expression * expr,int param,const char * key,const char * value)131efe51d0cSJohn Levon void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
132efe51d0cSJohn Levon {
133efe51d0cSJohn Levon 	mark_untracked_lost(expr, param, key, UNTRACKED_PARAM);
134efe51d0cSJohn Levon }
135efe51d0cSJohn Levon 
mark_lost(struct expression * expr,int param,const char * key,const char * value)136efe51d0cSJohn Levon void mark_lost(struct expression *expr, int param, const char *key, const char *value)
137efe51d0cSJohn Levon {
138efe51d0cSJohn Levon 	mark_untracked_lost(expr, param, key, LOST_PARAM);
1391f5207b7SJohn Levon }
1401f5207b7SJohn Levon 
lost_in_va_args(struct expression * expr)1411f5207b7SJohn Levon static int lost_in_va_args(struct expression *expr)
1421f5207b7SJohn Levon {
1431f5207b7SJohn Levon 	struct symbol *fn;
1441f5207b7SJohn Levon 	char *name;
1451f5207b7SJohn Levon 	int is_lost;
1461f5207b7SJohn Levon 
1471f5207b7SJohn Levon 	fn = get_type(expr->fn);
1481f5207b7SJohn Levon 	if (!fn || !fn->variadic)
1491f5207b7SJohn Levon 		return 0;
1501f5207b7SJohn Levon 
1511f5207b7SJohn Levon 	is_lost = 1;
1521f5207b7SJohn Levon 	name = expr_to_var(expr->fn);
1531f5207b7SJohn Levon 	if (name && strstr(name, "print"))
1541f5207b7SJohn Levon 		is_lost = 0;
1551f5207b7SJohn Levon 	free_string(name);
1561f5207b7SJohn Levon 
1571f5207b7SJohn Levon 	return is_lost;
1581f5207b7SJohn Levon }
1591f5207b7SJohn Levon 
match_after_call(struct expression * expr)1601f5207b7SJohn Levon static void match_after_call(struct expression *expr)
1611f5207b7SJohn Levon {
1621f5207b7SJohn Levon 	struct expression *arg;
1631f5207b7SJohn Levon 	struct symbol *type;
1641f5207b7SJohn Levon 	int i;
1651f5207b7SJohn Levon 
1661f5207b7SJohn Levon 	if (lost_in_va_args(expr))
1671f5207b7SJohn Levon 		tracked = 0;
1681f5207b7SJohn Levon 
1691f5207b7SJohn Levon 	if (tracked) {
1701f5207b7SJohn Levon 		tracked = 0;
1711f5207b7SJohn Levon 		return;
1721f5207b7SJohn Levon 	}
1731f5207b7SJohn Levon 
1741f5207b7SJohn Levon 	i = -1;
1751f5207b7SJohn Levon 	FOR_EACH_PTR(expr->args, arg) {
1761f5207b7SJohn Levon 		i++;
1771f5207b7SJohn Levon 
1781f5207b7SJohn Levon 		type = get_type(arg);
1791f5207b7SJohn Levon 		if (!type || type->type != SYM_PTR)
1801f5207b7SJohn Levon 			continue;
1811f5207b7SJohn Levon 
1821f5207b7SJohn Levon 		call_untracked_callbacks(expr, i);
1831f5207b7SJohn Levon 		set_state_expr(my_id, arg, &untracked);
1841f5207b7SJohn Levon 	} END_FOR_EACH_PTR(arg);
1851f5207b7SJohn Levon }
1861f5207b7SJohn Levon 
187efe51d0cSJohn Levon 
mark_all_params(int return_id,char * return_ranges,int type)188efe51d0cSJohn Levon static void mark_all_params(int return_id, char *return_ranges, int type)
1891f5207b7SJohn Levon {
1901f5207b7SJohn Levon 	struct symbol *arg;
1911f5207b7SJohn Levon 	int param;
1921f5207b7SJohn Levon 
1931f5207b7SJohn Levon 	param = -1;
1941f5207b7SJohn Levon 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
1951f5207b7SJohn Levon 		param++;
1961f5207b7SJohn Levon 
1971f5207b7SJohn Levon 		if (!arg->ident)
1981f5207b7SJohn Levon 			continue;
1991f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges,
200efe51d0cSJohn Levon 					 type, param, "$", "");
2011f5207b7SJohn Levon 	} END_FOR_EACH_PTR(arg);
2021f5207b7SJohn Levon }
2031f5207b7SJohn Levon 
204efe51d0cSJohn Levon 
mark_all_params_untracked(int return_id,char * return_ranges,struct expression * expr)205efe51d0cSJohn Levon void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
206efe51d0cSJohn Levon {
207efe51d0cSJohn Levon 	mark_all_params(return_id, return_ranges, UNTRACKED_PARAM);
208efe51d0cSJohn Levon }
209efe51d0cSJohn Levon 
mark_all_params_lost(int return_id,char * return_ranges,struct expression * expr)210efe51d0cSJohn Levon void mark_all_params_lost(int return_id, char *return_ranges, struct expression *expr)
211efe51d0cSJohn Levon {
212efe51d0cSJohn Levon 	mark_all_params(return_id, return_ranges, LOST_PARAM);
213efe51d0cSJohn Levon }
214efe51d0cSJohn Levon 
print_untracked_params(int return_id,char * return_ranges,struct expression * expr)2151f5207b7SJohn Levon static void print_untracked_params(int return_id, char *return_ranges, struct expression *expr)
2161f5207b7SJohn Levon {
217efe51d0cSJohn Levon 	struct sm_state *sm;
2181f5207b7SJohn Levon 	struct symbol *arg;
2191f5207b7SJohn Levon 	int param;
220efe51d0cSJohn Levon 	int type;
2211f5207b7SJohn Levon 
2221f5207b7SJohn Levon 	param = -1;
2231f5207b7SJohn Levon 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
2241f5207b7SJohn Levon 		param++;
2251f5207b7SJohn Levon 
2261f5207b7SJohn Levon 		if (!arg->ident)
2271f5207b7SJohn Levon 			continue;
228efe51d0cSJohn Levon 
229efe51d0cSJohn Levon 		if (__bail_on_rest_of_function) {
230efe51d0cSJohn Levon 			/* hairy functions are lost */
231efe51d0cSJohn Levon 			type = LOST_PARAM;
232efe51d0cSJohn Levon 		} else if ((sm = get_sm_state(my_id, arg->ident->name, arg))) {
233efe51d0cSJohn Levon 			if (slist_has_state(sm->possible, &lost))
234efe51d0cSJohn Levon 				type = LOST_PARAM;
235efe51d0cSJohn Levon 			else
236efe51d0cSJohn Levon 				type = UNTRACKED_PARAM;
237efe51d0cSJohn Levon 		} else {
2381f5207b7SJohn Levon 			continue;
239efe51d0cSJohn Levon 		}
2401f5207b7SJohn Levon 
2411f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges,
242efe51d0cSJohn Levon 					 type, param, "$", "");
2431f5207b7SJohn Levon 	} END_FOR_EACH_PTR(arg);
2441f5207b7SJohn Levon }
2451f5207b7SJohn Levon 
match_param_assign(struct expression * expr)2461f5207b7SJohn Levon static void match_param_assign(struct expression *expr)
2471f5207b7SJohn Levon {
2481f5207b7SJohn Levon 	struct expression *right;
2491f5207b7SJohn Levon 	struct symbol *type;
2501f5207b7SJohn Levon 	int param;
2511f5207b7SJohn Levon 
2521f5207b7SJohn Levon 	if (__in_fake_assign)
2531f5207b7SJohn Levon 		return;
2541f5207b7SJohn Levon 
2551f5207b7SJohn Levon 	right = strip_expr(expr->right);
2561f5207b7SJohn Levon 	type = get_type(right);
2571f5207b7SJohn Levon 	if (!type || type->type != SYM_PTR)
2581f5207b7SJohn Levon 		return;
2591f5207b7SJohn Levon 
2601f5207b7SJohn Levon 	param = get_param_num(right);
2611f5207b7SJohn Levon 	if (param < 0)
2621f5207b7SJohn Levon 		return;
2631f5207b7SJohn Levon 
2641f5207b7SJohn Levon 	set_state_expr(my_id, right, &untracked);
2651f5207b7SJohn Levon }
2661f5207b7SJohn Levon 
2671f5207b7SJohn Levon 
match_param_assign_in_asm(struct statement * stmt)2681f5207b7SJohn Levon static void match_param_assign_in_asm(struct statement *stmt)
2691f5207b7SJohn Levon {
2701f5207b7SJohn Levon 
271*c85f09ccSJohn Levon 	struct expression *tmp, *expr;
2721f5207b7SJohn Levon 	struct symbol *type;
2731f5207b7SJohn Levon 	int param;
2741f5207b7SJohn Levon 
275*c85f09ccSJohn Levon 	FOR_EACH_PTR(stmt->asm_inputs, tmp) {
276*c85f09ccSJohn Levon 		expr = strip_expr(tmp->expr);
277*c85f09ccSJohn Levon 		type = get_type(expr);
278*c85f09ccSJohn Levon 		if (!type || type->type != SYM_PTR)
2791f5207b7SJohn Levon 			continue;
280*c85f09ccSJohn Levon 		param = get_param_num(expr);
281*c85f09ccSJohn Levon 		if (param < 0)
2821f5207b7SJohn Levon 			continue;
283*c85f09ccSJohn Levon 		set_state_expr(my_id, expr, &untracked);
284*c85f09ccSJohn Levon 	} END_FOR_EACH_PTR(tmp);
2851f5207b7SJohn Levon }
2861f5207b7SJohn Levon 
match_inline_start(struct expression * expr)2871f5207b7SJohn Levon static void match_inline_start(struct expression *expr)
2881f5207b7SJohn Levon {
2891f5207b7SJohn Levon 	push_int(&tracked_stack, tracked);
2901f5207b7SJohn Levon }
2911f5207b7SJohn Levon 
match_inline_end(struct expression * expr)2921f5207b7SJohn Levon static void match_inline_end(struct expression *expr)
2931f5207b7SJohn Levon {
2941f5207b7SJohn Levon 	tracked = pop_int(&tracked_stack);
2951f5207b7SJohn Levon }
2961f5207b7SJohn Levon 
register_untracked_param(int id)2971f5207b7SJohn Levon void register_untracked_param(int id)
2981f5207b7SJohn Levon {
2991f5207b7SJohn Levon 	my_id = id;
3001f5207b7SJohn Levon 
3011f5207b7SJohn Levon 	select_return_states_hook(INTERNAL, &assume_tracked);
3021f5207b7SJohn Levon 	select_return_states_hook(UNTRACKED_PARAM, &mark_untracked);
303efe51d0cSJohn Levon 	select_return_states_hook(LOST_PARAM, &mark_lost);
3041f5207b7SJohn Levon 	add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB);
3051f5207b7SJohn Levon 
3061f5207b7SJohn Levon 	add_split_return_callback(&print_untracked_params);
3071f5207b7SJohn Levon 
3081f5207b7SJohn Levon 	add_hook(&match_param_assign, ASSIGNMENT_HOOK);
3091f5207b7SJohn Levon 	add_hook(&match_param_assign_in_asm, ASM_HOOK);
3101f5207b7SJohn Levon 
3111f5207b7SJohn Levon 	add_hook(&match_inline_start, INLINE_FN_START);
3121f5207b7SJohn Levon 	add_hook(&match_inline_end, INLINE_FN_END);
3131f5207b7SJohn Levon }
314