1 /*
2  * Copyright (C) 2017 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  * Say you have assign a function to a function pointer and you assign a
20  * pointer to the data argument then we want to record some information about
21  * the argument.  Right now what I mainly want to record is the type of it, I
22  * guess.
23  *
24  */
25 
26 #include "smatch.h"
27 #include "smatch_extra.h"
28 #include "smatch_slist.h"
29 #include <ctype.h>
30 
31 static int my_id;
32 
assigns_parameters(struct expression * fn,struct expression * arg)33 static int assigns_parameters(struct expression *fn, struct expression *arg)
34 {
35 	int fn_param, arg_param;
36 	char buf[32];
37 
38 	fn_param = get_param_num(fn);
39 	if (fn_param < 0)
40 		return 0;
41 
42 	arg_param = get_param_num(arg);
43 	if (arg_param < 0)
44 		return 0;
45 
46 	snprintf(buf, sizeof(buf), "%d", arg_param);
47 	sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
48 	return 1;
49 }
50 
link_function_arg(struct expression * fn,int param,struct expression * arg)51 static void link_function_arg(struct expression *fn, int param, struct expression *arg)
52 {
53 	struct symbol *type;
54 
55 	if (!fn || !arg)
56 		return;
57 	if (assigns_parameters(fn, arg))
58 		return;
59 
60 	type = get_type(arg);
61 	if (!type || type->type != SYM_PTR)
62 		return;
63 	type = get_real_base_type(type);
64 	if (!type)
65 		return;
66 	// FIXME: param shouldn't always be 0?
67 	sql_insert_fn_data_link(fn, PASSES_TYPE, param, "$", type_to_str(type));
68 }
69 
70 char *next_param_name;
71 struct symbol *next_param_sym;
72 struct expression *next_fn;
match_assign_param(struct expression * expr)73 static void match_assign_param(struct expression *expr)
74 {
75 	struct symbol *sym;
76 	char *name;
77 
78 	if (!next_param_name)
79 		return;
80 
81 	name = expr_to_var_sym(expr->left, &sym);
82 	if (!name || !sym) {
83 		free_string(name);
84 		return;
85 	}
86 
87 	if (sym != next_param_sym ||
88 	    strcmp(name, next_param_name) != 0)
89 		return;
90 
91 	link_function_arg(next_fn, 0, strip_expr(expr->right));
92 
93 	next_param_name = 0;
94 	next_param_sym = NULL;
95 	next_fn = NULL;
96 }
97 
get_arg_ptr(void * _arg_ptr,int argc,char ** argv,char ** azColName)98 static int get_arg_ptr(void *_arg_ptr, int argc, char **argv, char **azColName)
99 {
100 	char **arg_ptr = _arg_ptr;
101 
102 	*arg_ptr = NULL;
103 	if (argc != 1)
104 		return 0;
105 	*arg_ptr = alloc_string(argv[0]);
106 	return 0;
107 }
108 
get_data_member(char * fn_member,struct expression * expr,struct symbol ** sym)109 static char *get_data_member(char *fn_member, struct expression *expr, struct symbol **sym)
110 {
111 	struct symbol *tmp_sym;
112 	char *fn_str;
113 	char *arg_ptr = NULL;
114 	char *end_type;
115 	int len_ptr, len_str;
116 	char buf[128];
117 
118 	*sym = NULL;
119 	run_sql(get_arg_ptr, &arg_ptr,
120 		"select data from fn_ptr_data_link where fn_ptr = '%s';", fn_member);
121 	if (!arg_ptr)
122 		return NULL;
123 	end_type = strchr(arg_ptr, '>');
124 	if (!end_type)
125 		return NULL;
126 	end_type++;
127 	fn_str = expr_to_var_sym(expr, &tmp_sym);
128 	if (!fn_str || !tmp_sym)
129 		return NULL;
130 	len_ptr = strlen(fn_member);
131 	len_str = strlen(fn_str);
132 	while (len_str > 0 && len_ptr > 0) {
133 		if (fn_str[len_str - 1] != fn_member[len_ptr - 1])
134 			break;
135 		if (fn_str[len_str - 1] == '>')
136 			break;
137 		len_str--;
138 		len_ptr--;
139 	}
140 
141 	strncpy(buf, fn_str, sizeof(buf));
142 	snprintf(buf + len_str, sizeof(buf) - len_str, "%s", end_type);
143 	*sym = tmp_sym;
144 	return alloc_string(buf);
145 }
146 
match_assign_function(struct expression * expr)147 static void match_assign_function(struct expression *expr)
148 {
149 	struct expression *right, *arg;
150 	struct symbol *sym;
151 	char *data_member;
152 	struct symbol *type;
153 	char *member_name;
154 
155 	right = strip_expr(expr->right);
156 	if (right->type == EXPR_PREOP && right->op == '&')
157 		right = strip_expr(right->unop);
158 
159 	type = get_type(right);
160 	if (type && type->type == SYM_PTR)
161 		type = get_real_base_type(type);
162 	if (!type || type->type != SYM_FN)
163 		return;
164 
165 	member_name = get_member_name(expr->left);
166 	if (!member_name)
167 		return;
168 
169 	data_member = get_data_member(member_name, expr->left, &sym);
170 	if (!data_member || !sym) {
171 		free_string(data_member);
172 		data_member = NULL;
173 	}
174 
175 	arg = get_assigned_expr_name_sym(data_member, sym);
176 	if (arg) {
177 		link_function_arg(right, 0, arg);
178 	} else {
179 		next_param_name = data_member;
180 		next_param_sym = sym;
181 		next_fn = right;
182 	}
183 }
184 
is_recursive_call(struct expression * call)185 static int is_recursive_call(struct expression *call)
186 {
187 	if (call->fn->type != EXPR_SYMBOL)
188 		return 0;
189 	if (call->fn->symbol == cur_func_sym)
190 		return 1;
191 	return 0;
192 }
193 
check_passes_fn_and_data(struct expression * call,struct expression * fn,char * key,char * value)194 static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value)
195 {
196 	struct expression *arg;
197 	struct symbol *type;
198 	int data_nr;
199 
200 	if (is_recursive_call(call))
201 		return;
202 
203 	type = get_type(fn);
204 	if (!type || type->type != SYM_FN)
205 		return;
206 
207 	if (!isdigit(value[0]))
208 		return;
209 	data_nr = atoi(value);
210 	arg = get_argument_from_call_expr(call->args, data_nr);
211 	if (!arg)
212 		return;
213 	link_function_arg(fn, 0, arg);
214 }
215 
match_end_func(struct symbol * sym)216 static void match_end_func(struct symbol *sym)
217 {
218 	next_param_sym = NULL;
219 	next_fn = NULL;
220 }
221 
register_about_fn_ptr_arg(int id)222 void register_about_fn_ptr_arg(int id)
223 {
224 	my_id = id;
225 
226 	if (0 && !option_info)
227 		return;
228 	add_hook(match_assign_param, ASSIGNMENT_HOOK);
229 	add_hook(match_assign_function, ASSIGNMENT_HOOK);
230 	select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data);
231 	add_hook(&match_end_func, END_FUNC_HOOK);
232 }
233