11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2017 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  * Say you have assign a function to a function pointer and you assign a
201f5207b7SJohn Levon  * pointer to the data argument then we want to record some information about
211f5207b7SJohn Levon  * the argument.  Right now what I mainly want to record is the type of it, I
221f5207b7SJohn Levon  * guess.
231f5207b7SJohn Levon  *
241f5207b7SJohn Levon  */
251f5207b7SJohn Levon 
261f5207b7SJohn Levon #include "smatch.h"
271f5207b7SJohn Levon #include "smatch_extra.h"
281f5207b7SJohn Levon #include "smatch_slist.h"
291f5207b7SJohn Levon #include <ctype.h>
301f5207b7SJohn Levon 
311f5207b7SJohn Levon static int my_id;
321f5207b7SJohn Levon 
assigns_parameters(struct expression * fn,struct expression * arg)331f5207b7SJohn Levon static int assigns_parameters(struct expression *fn, struct expression *arg)
341f5207b7SJohn Levon {
351f5207b7SJohn Levon 	int fn_param, arg_param;
361f5207b7SJohn Levon 	char buf[32];
371f5207b7SJohn Levon 
381f5207b7SJohn Levon 	fn_param = get_param_num(fn);
391f5207b7SJohn Levon 	if (fn_param < 0)
401f5207b7SJohn Levon 		return 0;
411f5207b7SJohn Levon 
421f5207b7SJohn Levon 	arg_param = get_param_num(arg);
431f5207b7SJohn Levon 	if (arg_param < 0)
441f5207b7SJohn Levon 		return 0;
451f5207b7SJohn Levon 
461f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%d", arg_param);
471f5207b7SJohn Levon 	sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
481f5207b7SJohn Levon 	return 1;
491f5207b7SJohn Levon }
501f5207b7SJohn Levon 
link_function_arg(struct expression * fn,int param,struct expression * arg)511f5207b7SJohn Levon static void link_function_arg(struct expression *fn, int param, struct expression *arg)
521f5207b7SJohn Levon {
531f5207b7SJohn Levon 	struct symbol *type;
541f5207b7SJohn Levon 
551f5207b7SJohn Levon 	if (!fn || !arg)
561f5207b7SJohn Levon 		return;
571f5207b7SJohn Levon 	if (assigns_parameters(fn, arg))
581f5207b7SJohn Levon 		return;
591f5207b7SJohn Levon 
601f5207b7SJohn Levon 	type = get_type(arg);
611f5207b7SJohn Levon 	if (!type || type->type != SYM_PTR)
621f5207b7SJohn Levon 		return;
631f5207b7SJohn Levon 	type = get_real_base_type(type);
641f5207b7SJohn Levon 	if (!type)
651f5207b7SJohn Levon 		return;
661f5207b7SJohn Levon 	// FIXME: param shouldn't always be 0?
671f5207b7SJohn Levon 	sql_insert_fn_data_link(fn, PASSES_TYPE, param, "$", type_to_str(type));
681f5207b7SJohn Levon }
691f5207b7SJohn Levon 
701f5207b7SJohn Levon char *next_param_name;
711f5207b7SJohn Levon struct symbol *next_param_sym;
721f5207b7SJohn Levon struct expression *next_fn;
match_assign_param(struct expression * expr)731f5207b7SJohn Levon static void match_assign_param(struct expression *expr)
741f5207b7SJohn Levon {
751f5207b7SJohn Levon 	struct symbol *sym;
761f5207b7SJohn Levon 	char *name;
771f5207b7SJohn Levon 
781f5207b7SJohn Levon 	if (!next_param_name)
791f5207b7SJohn Levon 		return;
801f5207b7SJohn Levon 
811f5207b7SJohn Levon 	name = expr_to_var_sym(expr->left, &sym);
821f5207b7SJohn Levon 	if (!name || !sym) {
831f5207b7SJohn Levon 		free_string(name);
841f5207b7SJohn Levon 		return;
851f5207b7SJohn Levon 	}
861f5207b7SJohn Levon 
871f5207b7SJohn Levon 	if (sym != next_param_sym ||
881f5207b7SJohn Levon 	    strcmp(name, next_param_name) != 0)
891f5207b7SJohn Levon 		return;
901f5207b7SJohn Levon 
911f5207b7SJohn Levon 	link_function_arg(next_fn, 0, strip_expr(expr->right));
921f5207b7SJohn Levon 
931f5207b7SJohn Levon 	next_param_name = 0;
941f5207b7SJohn Levon 	next_param_sym = NULL;
951f5207b7SJohn Levon 	next_fn = NULL;
961f5207b7SJohn Levon }
971f5207b7SJohn Levon 
get_arg_ptr(void * _arg_ptr,int argc,char ** argv,char ** azColName)981f5207b7SJohn Levon static int get_arg_ptr(void *_arg_ptr, int argc, char **argv, char **azColName)
991f5207b7SJohn Levon {
1001f5207b7SJohn Levon 	char **arg_ptr = _arg_ptr;
1011f5207b7SJohn Levon 
1021f5207b7SJohn Levon 	*arg_ptr = NULL;
1031f5207b7SJohn Levon 	if (argc != 1)
1041f5207b7SJohn Levon 		return 0;
1051f5207b7SJohn Levon 	*arg_ptr = alloc_string(argv[0]);
1061f5207b7SJohn Levon 	return 0;
1071f5207b7SJohn Levon }
1081f5207b7SJohn Levon 
get_data_member(char * fn_member,struct expression * expr,struct symbol ** sym)1091f5207b7SJohn Levon static char *get_data_member(char *fn_member, struct expression *expr, struct symbol **sym)
1101f5207b7SJohn Levon {
1111f5207b7SJohn Levon 	struct symbol *tmp_sym;
1121f5207b7SJohn Levon 	char *fn_str;
1131f5207b7SJohn Levon 	char *arg_ptr = NULL;
1141f5207b7SJohn Levon 	char *end_type;
1151f5207b7SJohn Levon 	int len_ptr, len_str;
1161f5207b7SJohn Levon 	char buf[128];
1171f5207b7SJohn Levon 
1181f5207b7SJohn Levon 	*sym = NULL;
1191f5207b7SJohn Levon 	run_sql(get_arg_ptr, &arg_ptr,
1201f5207b7SJohn Levon 		"select data from fn_ptr_data_link where fn_ptr = '%s';", fn_member);
1211f5207b7SJohn Levon 	if (!arg_ptr)
1221f5207b7SJohn Levon 		return NULL;
1231f5207b7SJohn Levon 	end_type = strchr(arg_ptr, '>');
1241f5207b7SJohn Levon 	if (!end_type)
1251f5207b7SJohn Levon 		return NULL;
1261f5207b7SJohn Levon 	end_type++;
1271f5207b7SJohn Levon 	fn_str = expr_to_var_sym(expr, &tmp_sym);
1281f5207b7SJohn Levon 	if (!fn_str || !tmp_sym)
1291f5207b7SJohn Levon 		return NULL;
1301f5207b7SJohn Levon 	len_ptr = strlen(fn_member);
1311f5207b7SJohn Levon 	len_str = strlen(fn_str);
1321f5207b7SJohn Levon 	while (len_str > 0 && len_ptr > 0) {
1331f5207b7SJohn Levon 		if (fn_str[len_str - 1] != fn_member[len_ptr - 1])
1341f5207b7SJohn Levon 			break;
1351f5207b7SJohn Levon 		if (fn_str[len_str - 1] == '>')
1361f5207b7SJohn Levon 			break;
1371f5207b7SJohn Levon 		len_str--;
1381f5207b7SJohn Levon 		len_ptr--;
1391f5207b7SJohn Levon 	}
1401f5207b7SJohn Levon 
1411f5207b7SJohn Levon 	strncpy(buf, fn_str, sizeof(buf));
142*efe51d0cSJohn Levon 	snprintf(buf + len_str, sizeof(buf) - len_str, "%s", end_type);
1431f5207b7SJohn Levon 	*sym = tmp_sym;
1441f5207b7SJohn Levon 	return alloc_string(buf);
1451f5207b7SJohn Levon }
1461f5207b7SJohn Levon 
match_assign_function(struct expression * expr)1471f5207b7SJohn Levon static void match_assign_function(struct expression *expr)
1481f5207b7SJohn Levon {
1491f5207b7SJohn Levon 	struct expression *right, *arg;
1501f5207b7SJohn Levon 	struct symbol *sym;
1511f5207b7SJohn Levon 	char *data_member;
1521f5207b7SJohn Levon 	struct symbol *type;
1531f5207b7SJohn Levon 	char *member_name;
1541f5207b7SJohn Levon 
1551f5207b7SJohn Levon 	right = strip_expr(expr->right);
1561f5207b7SJohn Levon 	if (right->type == EXPR_PREOP && right->op == '&')
1571f5207b7SJohn Levon 		right = strip_expr(right->unop);
1581f5207b7SJohn Levon 
1591f5207b7SJohn Levon 	type = get_type(right);
1601f5207b7SJohn Levon 	if (type && type->type == SYM_PTR)
1611f5207b7SJohn Levon 		type = get_real_base_type(type);
1621f5207b7SJohn Levon 	if (!type || type->type != SYM_FN)
1631f5207b7SJohn Levon 		return;
1641f5207b7SJohn Levon 
1651f5207b7SJohn Levon 	member_name = get_member_name(expr->left);
1661f5207b7SJohn Levon 	if (!member_name)
1671f5207b7SJohn Levon 		return;
1681f5207b7SJohn Levon 
1691f5207b7SJohn Levon 	data_member = get_data_member(member_name, expr->left, &sym);
1701f5207b7SJohn Levon 	if (!data_member || !sym) {
1711f5207b7SJohn Levon 		free_string(data_member);
1721f5207b7SJohn Levon 		data_member = NULL;
1731f5207b7SJohn Levon 	}
1741f5207b7SJohn Levon 
1751f5207b7SJohn Levon 	arg = get_assigned_expr_name_sym(data_member, sym);
1761f5207b7SJohn Levon 	if (arg) {
1771f5207b7SJohn Levon 		link_function_arg(right, 0, arg);
1781f5207b7SJohn Levon 	} else {
1791f5207b7SJohn Levon 		next_param_name = data_member;
1801f5207b7SJohn Levon 		next_param_sym = sym;
1811f5207b7SJohn Levon 		next_fn = right;
1821f5207b7SJohn Levon 	}
1831f5207b7SJohn Levon }
1841f5207b7SJohn Levon 
is_recursive_call(struct expression * call)1851f5207b7SJohn Levon static int is_recursive_call(struct expression *call)
1861f5207b7SJohn Levon {
1871f5207b7SJohn Levon 	if (call->fn->type != EXPR_SYMBOL)
1881f5207b7SJohn Levon 		return 0;
1891f5207b7SJohn Levon 	if (call->fn->symbol == cur_func_sym)
1901f5207b7SJohn Levon 		return 1;
1911f5207b7SJohn Levon 	return 0;
1921f5207b7SJohn Levon }
1931f5207b7SJohn Levon 
check_passes_fn_and_data(struct expression * call,struct expression * fn,char * key,char * value)1941f5207b7SJohn Levon static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value)
1951f5207b7SJohn Levon {
1961f5207b7SJohn Levon 	struct expression *arg;
1971f5207b7SJohn Levon 	struct symbol *type;
1981f5207b7SJohn Levon 	int data_nr;
1991f5207b7SJohn Levon 
2001f5207b7SJohn Levon 	if (is_recursive_call(call))
2011f5207b7SJohn Levon 		return;
2021f5207b7SJohn Levon 
2031f5207b7SJohn Levon 	type = get_type(fn);
2041f5207b7SJohn Levon 	if (!type || type->type != SYM_FN)
2051f5207b7SJohn Levon 		return;
2061f5207b7SJohn Levon 
2071f5207b7SJohn Levon 	if (!isdigit(value[0]))
2081f5207b7SJohn Levon 		return;
2091f5207b7SJohn Levon 	data_nr = atoi(value);
2101f5207b7SJohn Levon 	arg = get_argument_from_call_expr(call->args, data_nr);
2111f5207b7SJohn Levon 	if (!arg)
2121f5207b7SJohn Levon 		return;
2131f5207b7SJohn Levon 	link_function_arg(fn, 0, arg);
2141f5207b7SJohn Levon }
2151f5207b7SJohn Levon 
match_end_func(struct symbol * sym)2161f5207b7SJohn Levon static void match_end_func(struct symbol *sym)
2171f5207b7SJohn Levon {
2181f5207b7SJohn Levon 	next_param_sym = NULL;
2191f5207b7SJohn Levon 	next_fn = NULL;
2201f5207b7SJohn Levon }
2211f5207b7SJohn Levon 
register_about_fn_ptr_arg(int id)2221f5207b7SJohn Levon void register_about_fn_ptr_arg(int id)
2231f5207b7SJohn Levon {
2241f5207b7SJohn Levon 	my_id = id;
2251f5207b7SJohn Levon 
2261f5207b7SJohn Levon 	if (0 && !option_info)
2271f5207b7SJohn Levon 		return;
2281f5207b7SJohn Levon 	add_hook(match_assign_param, ASSIGNMENT_HOOK);
2291f5207b7SJohn Levon 	add_hook(match_assign_function, ASSIGNMENT_HOOK);
2301f5207b7SJohn Levon 	select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data);
2311f5207b7SJohn Levon 	add_hook(&match_end_func, END_FUNC_HOOK);
2321f5207b7SJohn Levon }
233