1 /*
2  * Copyright (C) 2016 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  * What we're trying to do here is record links between function pointers and
20  * function data.  If you have foo->function(foo->data); that's very easy.  But
21  * the problem is maybe when you pass the function and the data as parameters.
22  *
23  */
24 
25 #include "smatch.h"
26 #include <ctype.h>
27 
28 static int my_id;
29 
save_in_fn_ptr_data_link_table(struct expression * fn,struct expression * arg)30 static void save_in_fn_ptr_data_link_table(struct expression *fn, struct expression *arg)
31 {
32 	struct symbol *fn_sym, *arg_sym;
33 	struct symbol *type;
34 	char *fn_name, *arg_name;
35 	int sym_len;
36 	char fn_buf[128];
37 	char arg_buf[128];
38 
39 	fn_name = expr_to_var_sym(fn, &fn_sym);
40 	arg_name = expr_to_var_sym(arg, &arg_sym);
41 	if (!fn_sym || !fn_sym->ident || !arg_sym || !fn_name || !arg_name)
42 		goto free;
43 	if (fn_sym != arg_sym)
44 		goto free;
45 
46 	sym_len = fn_sym->ident->len;
47 
48 	/* This is ignoring
49 	 * net/mac80211/driver-ops.h:482 drv_sta_remove() FN: local->ops->sta_remove ARG: &local->hw
50 	 * but ideally the restriction can be removed later.
51 	 */
52 	if (strncmp(fn_name, arg_name, sym_len) != 0)
53 		goto free;
54 
55 	type = get_real_base_type(fn_sym);
56 	if (!type)
57 		goto free;
58 	if (type->type != SYM_PTR)
59 		goto free;
60 	type = get_real_base_type(type);
61 	if (!type || type->type != SYM_STRUCT || !type->ident)
62 		goto free;
63 
64 	snprintf(fn_buf, sizeof(fn_buf), "(struct %s)%s", type->ident->name,
65 		 fn_name + sym_len);
66 
67 	snprintf(arg_buf, sizeof(arg_buf), "(struct %s)%s", type->ident->name,
68 		 arg_name + sym_len);
69 
70 	sql_insert_fn_ptr_data_link(fn_buf, arg_buf);
71 free:
72 	free_string(arg_name);
73 	free_string(fn_name);
74 }
75 
print_calls_parameter(struct expression * call)76 static int print_calls_parameter(struct expression *call)
77 {
78 	struct expression *arg;
79 	int fn_param, arg_param;
80 	char buf[32];
81 
82 	fn_param = get_param_num(call->fn);
83 	if (fn_param < 0)
84 		return 0;
85 
86 	arg = get_argument_from_call_expr(call->args, 0);
87 	if (!arg)
88 		return 0;
89 
90 	arg_param = get_param_num(arg);
91 	if (arg_param < 0)
92 		return 0;
93 
94 	snprintf(buf, sizeof(buf), "%d", arg_param);
95 	sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
96 	return 0;
97 }
98 
print_call_is_linked(struct expression * call)99 static int print_call_is_linked(struct expression *call)
100 {
101 	struct expression *fn, *tmp;
102 	struct expression *arg;
103 	struct symbol *fn_sym;
104 	struct symbol *arg_sym = NULL;
105 	int i;
106 
107 	fn = strip_expr(call->fn);
108 	tmp = get_assigned_expr(fn);
109 	if (tmp)
110 		fn = tmp;
111 	if (fn->type != EXPR_DEREF || !fn->member)
112 		return 0;
113 
114 	fn_sym = expr_to_sym(fn);
115 	if (!fn_sym)
116 		return 0;
117 
118 	i = -1;
119 	FOR_EACH_PTR(call->args, arg) {
120 		i++;
121 		tmp = get_assigned_expr(arg);
122 		if (tmp)
123 			arg = tmp;
124 		arg_sym = expr_to_sym(arg);
125 		if (arg_sym == fn_sym) {
126 			save_in_fn_ptr_data_link_table(fn, arg);
127 			return 1;
128 		}
129 	} END_FOR_EACH_PTR(arg);
130 
131 	return 0;
132 }
133 
is_recursive_call(struct expression * call)134 static int is_recursive_call(struct expression *call)
135 {
136 	if (call->fn->type != EXPR_SYMBOL)
137 		return 0;
138 	if (call->fn->symbol == cur_func_sym)
139 		return 1;
140 	return 0;
141 }
142 
check_passes_fn_and_data(struct expression * call,struct expression * fn,char * key,char * value)143 static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value)
144 {
145 	struct expression *arg;
146 	struct expression *tmp;
147 	struct symbol *fn_sym, *arg_sym;
148 	struct symbol *type;
149 	int data_nr;
150 	int fn_param, arg_param;
151 
152 	if (is_recursive_call(call))
153 		return;
154 
155 	type = get_type(fn);
156 	if (!type || type->type != SYM_PTR)
157 		return;
158 	type = get_real_base_type(type);
159 	if (!type || type->type != SYM_FN)
160 		return;
161 	tmp = get_assigned_expr(fn);
162 	if (tmp)
163 		fn = tmp;
164 
165 	if (!isdigit(value[0]))
166 		return;
167 	data_nr = atoi(value);
168 	arg = get_argument_from_call_expr(call->args, data_nr);
169 	if (!arg)
170 		return;
171 	tmp = get_assigned_expr(arg);
172 	if (tmp)
173 		arg = tmp;
174 
175 	fn_param = get_param_num(fn);
176 	arg_param = get_param_num(arg);
177 	if (fn_param >= 0 && arg_param >= 0) {
178 		char buf[32];
179 
180 		snprintf(buf, sizeof(buf), "%d", arg_param);
181 		sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
182 		return;
183 	}
184 
185 	fn_sym = expr_to_sym(fn);
186 	if (!fn_sym)
187 		return;
188 	arg_sym = expr_to_sym(arg);
189 	if (arg_sym != fn_sym)
190 		return;
191 	save_in_fn_ptr_data_link_table(fn, tmp);
192 }
193 
match_call_info(struct expression * call)194 static void match_call_info(struct expression *call)
195 {
196 	if (print_calls_parameter(call))
197 		return;
198 	if (print_call_is_linked(call))
199 		return;
200 }
201 
register_fn_arg_link(int id)202 void register_fn_arg_link(int id)
203 {
204 	my_id = id;
205 
206 	if (!option_info)
207 		return;
208 
209 	add_hook(&match_call_info, FUNCTION_CALL_HOOK);
210 	select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data);
211 }
212 
213