goto_tracker: still doesn't build
[smatch.git] / smatch_points_to_user_data.c
blob4267b85b53a7c3e9fe83ce034690e8dc82baa54c
1 /*
2 * Copyright (C) 2020 Oracle.
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.
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.
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
19 * The problem here is that we can have:
21 * p = skb->data;
23 * In the olden days we would just set "*p = 0-255" which meant that it pointed
24 * to user data. But then if we say "if (*p == 11) {" that means that "*p" is
25 * not user data any more, so then "*(p + 1)" is marked as not user data but it
26 * is.
28 * So now we've separated out the stuff that points to a user_buf from the other
29 * user data.
31 * There is a further complication because what if "p" points to a struct? In
32 * that case all the struct members are handled by smatch_kernel_user_data.c
33 * but we still need to keep in mind that "*(p + 1)" is user data. I'm not
34 * totally 100% sure how this will work.
36 * Generally a user pointer should be a void pointer, or an array etc. But if
37 * it points to a struct that can only be used for pointer math.
41 #include "smatch.h"
42 #include "smatch_slist.h"
43 #include "smatch_extra.h"
45 static int my_id;
46 STATE(user_data);
47 STATE(user_data_set);
49 static const char *returns_pointer_to_user_data[] = {
50 "nlmsg_data", "nla_data", "memdup_user", "kmap_atomic", "skb_network_header",
51 "cfg80211_find_elem_match", "ieee80211_bss_get_elem", "cfg80211_find_elem",
52 "ieee80211_bss_get_ie",
55 bool is_skb_data(struct expression *expr)
57 struct symbol *sym;
59 expr = strip_expr(expr);
60 if (!expr)
61 return false;
62 if (expr->type != EXPR_DEREF)
63 return false;
65 if (!expr->member)
66 return false;
67 if (strcmp(expr->member->name, "data") != 0)
68 return false;
70 sym = get_type(expr->deref);
71 if (!sym)
72 return false;
73 if (sym->type == SYM_PTR)
74 sym = get_real_base_type(sym);
75 if (!sym || sym->type != SYM_STRUCT || !sym->ident)
76 return false;
77 if (strcmp(sym->ident->name, "sk_buff") != 0)
78 return false;
80 return true;
83 bool is_user_data_fn(struct symbol *fn)
85 int i;
87 if (!fn || !fn->ident)
88 return false;
90 for (i = 0; i < ARRAY_SIZE(returns_pointer_to_user_data); i++) {
91 if (strcmp(fn->ident->name, returns_pointer_to_user_data[i]) == 0) {
92 // func_gets_user_data = true;
93 return true;
96 return false;
99 static bool is_points_to_user_data_fn(struct expression *expr)
101 expr = strip_expr(expr);
102 if (!expr || expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL ||
103 !expr->fn->symbol)
104 return false;
105 return is_user_data_fn(expr->fn->symbol);
108 static bool is_array_of_user_data(struct expression *expr)
110 struct expression *deref;
111 struct symbol *type;
113 if (expr->type == EXPR_PREOP && expr->op == '&') {
114 expr = strip_expr(expr->unop);
115 if (expr->type == EXPR_PREOP && expr->op == '*')
116 expr = strip_expr(expr->unop);
119 /* This is for array elements &foo->data[4] */
120 if (expr->type == EXPR_BINOP && expr->op == '+') {
121 if (points_to_user_data(expr->left))
122 return true;
123 if (points_to_user_data(expr->right))
124 return true;
127 /* This is for if you have: foo = skb->data; frob(foo->array); */
128 type = get_type(expr);
129 if (!type || type->type != SYM_ARRAY)
130 return false;
132 if (expr->type != EXPR_DEREF)
133 return false;
134 deref = strip_expr(expr->deref);
135 if (deref->type != EXPR_PREOP || deref->op != '*')
136 return false;
137 deref = strip_expr(deref->unop);
138 return points_to_user_data(deref);
141 bool points_to_user_data(struct expression *expr)
143 struct sm_state *sm;
145 expr = strip_expr(expr);
146 if (!expr)
147 return false;
149 if (expr->type == EXPR_POSTOP)
150 expr = strip_expr(expr->unop);
152 if (is_fake_call(expr))
153 return false;
155 if (expr->type == EXPR_ASSIGNMENT)
156 return points_to_user_data(expr->left);
158 if (is_array_of_user_data(expr))
159 return true;
161 if (expr->type == EXPR_BINOP && expr->op == '+')
162 expr = strip_expr(expr->left);
164 if (is_skb_data(expr))
165 return true;
167 if (is_points_to_user_data_fn(expr))
168 return true;
170 // FIXME if you have a struct pointer p then p->foo should be handled
171 // by smatch_kernel_user_data.c but if you have (p + 1)->foo then this
172 // should be handled here.
173 sm = get_sm_state_expr(my_id, expr);
174 if (!sm)
175 return false;
176 if (slist_has_state(sm->possible, &user_data) ||
177 slist_has_state(sm->possible, &user_data_set))
178 return true;
179 return false;
182 void set_points_to_user_data(struct expression *expr, bool is_new)
184 struct expression *tmp;
186 tmp = get_assigned_expr(expr);
187 if (tmp)
188 set_state_expr(my_id, tmp, is_new ? &user_data_set : &user_data);
189 set_state_expr(my_id, expr, is_new ? &user_data_set : &user_data);
192 static void match_assign(struct expression *expr)
194 if (is_fake_call(expr->right))
195 return;
197 if (!is_ptr_type(get_type(expr->left)))
198 return;
200 if (points_to_user_data(expr->right)) {
201 set_state_expr(my_id, expr->left, &user_data);
202 return;
205 if (get_state_expr(my_id, expr->left))
206 set_state_expr(my_id, expr->left, &undefined);
209 static void match_memcpy(const char *fn, struct expression *expr, void *_unused)
211 struct expression *dest, *src;
213 dest = get_argument_from_call_expr(expr->args, 0);
214 src = get_argument_from_call_expr(expr->args, 1);
216 if (points_to_user_data(src)) {
217 set_state_expr(my_id, expr->left, &user_data_set);
218 return;
221 if (get_state_expr(my_id, dest))
222 set_state_expr(my_id, dest, &undefined);
225 static void match_user_copy(const char *fn, struct expression *expr, void *_unused)
227 struct expression *dest, *size;
228 sval_t sval;
230 dest = get_argument_from_call_expr(expr->args, 0);
231 dest = strip_expr(dest);
232 if (!dest)
233 return;
235 size = get_argument_from_call_expr(expr->args, 2);
236 if (get_implied_value(size, &sval))
237 return;
239 set_state_expr(my_id, dest, &user_data_set);
242 static void return_info_callback(int return_id, char *return_ranges,
243 struct expression *returned_expr,
244 int param,
245 const char *printed_name,
246 struct sm_state *sm)
248 int type;
250 if (strncmp(printed_name, "&$", 2) == 0)
251 return;
253 if (is_socket_stuff(sm->sym))
254 return;
256 if (param >= 0) {
257 if (!slist_has_state(sm->possible, &user_data_set))
258 return;
259 type = USER_PTR_SET;
260 } else {
261 if (slist_has_state(sm->possible, &user_data_set))
262 type = USER_PTR_SET;
263 else if (slist_has_state(sm->possible, &user_data))
264 type = USER_PTR;
265 else
266 return;
268 if (parent_is_gone_var_sym(sm->name, sm->sym))
269 return;
271 sql_insert_return_states(return_id, return_ranges, type,
272 param, printed_name, "");
275 static void returns_user_ptr_helper(struct expression *expr, int param, char *key, char *value, bool set)
277 struct expression *arg;
278 struct expression *call;
279 char *name;
280 struct symbol *sym;
282 call = expr;
283 while (call->type == EXPR_ASSIGNMENT)
284 call = strip_expr(call->right);
285 if (call->type != EXPR_CALL)
286 return;
288 if (!set && !we_pass_user_data(call))
289 return;
291 if (param == -1) {
292 if (expr->type != EXPR_ASSIGNMENT) {
293 /* Nothing to do. Fake assignments should handle it */
294 return;
296 arg = expr->left;
297 goto set_user;
300 arg = get_argument_from_call_expr(call->args, param);
301 if (!arg)
302 return;
303 set_user:
304 name = get_variable_from_key(arg, key, &sym);
305 if (!name || !sym)
306 goto free;
307 if (set)
308 set_state(my_id, name, sym, &user_data_set);
309 else
310 set_state(my_id, name, sym, &user_data);
311 free:
312 free_string(name);
315 static void returns_user_ptr(struct expression *expr, int param, char *key, char *value)
317 returns_user_ptr_helper(expr, param, key, value, false);
320 static void returns_user_ptr_set(struct expression *expr, int param, char *key, char *value)
322 returns_user_ptr_helper(expr, param, key, value, true);
325 static void set_param_user_ptr(const char *name, struct symbol *sym, char *key, char *value)
327 struct expression *expr;
328 char *fullname;
330 expr = symbol_expression(sym);
331 fullname = get_variable_from_key(expr, key, NULL);
332 if (!fullname)
333 return;
334 set_state(my_id, fullname, sym, &user_data);
337 static void caller_info_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
339 if (is_socket_stuff(sm->sym))
340 return;
342 if (!slist_has_state(sm->possible, &user_data) &&
343 !slist_has_state(sm->possible, &user_data_set))
344 return;
345 sql_insert_caller_info(call, USER_PTR, param, printed_name, "");
348 void register_points_to_user_data(int id)
350 my_id = id;
352 if (option_project != PROJ_KERNEL)
353 return;
355 add_hook(&match_assign, ASSIGNMENT_HOOK);
357 add_function_hook("copy_from_user", &match_user_copy, NULL);
358 add_function_hook("memcpy_from_msg", &match_user_copy, NULL);
359 add_function_hook("__copy_from_user", &match_user_copy, NULL);
361 add_function_hook("memcpy", &match_memcpy, NULL);
362 add_function_hook("__memcpy", &match_memcpy, NULL);
364 add_caller_info_callback(my_id, caller_info_callback);
365 add_return_info_callback(my_id, return_info_callback);
367 select_caller_info_hook(set_param_user_ptr, USER_PTR);
368 select_return_states_hook(USER_PTR, &returns_user_ptr);
369 select_return_states_hook(USER_PTR_SET, &returns_user_ptr_set);