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