buf_comparison: s/kmemdup_user/memdup_user/
[smatch.git] / smatch_points_to_user_data.c
bloba0cf439df0f82b62a943af40bd482a5f6323d6dd
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);
48 static const char *returns_pointer_to_user_data[] = {
49 "nlmsg_data", "nla_data", "memdup_user", "kmap_atomic", "skb_network_header",
50 "cfg80211_find_elem_match", "ieee80211_bss_get_elem", "cfg80211_find_elem",
51 "ieee80211_bss_get_ie",
54 bool is_skb_data(struct expression *expr)
56 struct symbol *sym;
58 expr = strip_expr(expr);
59 if (!expr)
60 return false;
62 if (expr->type == EXPR_BINOP && expr->op == '+')
63 return is_skb_data(expr->left);
65 expr = strip_expr(expr);
66 if (!expr)
67 return false;
68 if (expr->type != EXPR_DEREF || expr->op != '.')
69 return false;
71 if (!expr->member)
72 return false;
73 if (strcmp(expr->member->name, "data") != 0)
74 return false;
76 sym = expr_to_sym(expr->deref);
77 if (!sym)
78 return false;
79 sym = get_real_base_type(sym);
80 if (!sym || sym->type != SYM_PTR)
81 return false;
82 sym = get_real_base_type(sym);
83 if (!sym || sym->type != SYM_STRUCT || !sym->ident)
84 return false;
85 if (strcmp(sym->ident->name, "sk_buff") != 0)
86 return false;
88 return true;
91 bool is_user_data_fn(struct symbol *fn)
93 int i;
95 if (!fn || !fn->ident)
96 return false;
98 for (i = 0; i < ARRAY_SIZE(returns_pointer_to_user_data); i++) {
99 if (strcmp(fn->ident->name, returns_pointer_to_user_data[i]) == 0) {
100 // func_gets_user_data = true;
101 return true;
104 return false;
107 static bool is_points_to_user_data_fn(struct expression *expr)
109 expr = strip_expr(expr);
110 if (!expr || expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL ||
111 !expr->fn->symbol)
112 return false;
113 return is_user_data_fn(expr->fn->symbol);
116 static bool is_array_of_user_data(struct expression *expr)
118 if (expr->type == EXPR_PREOP && expr->op == '&') {
119 expr = strip_expr(expr->unop);
120 if (expr->type == EXPR_PREOP && expr->op == '*')
121 expr = strip_expr(expr->unop);
124 if (expr->type == EXPR_BINOP && expr->op == '+') {
125 if (points_to_user_data(expr->left))
126 return true;
127 if (points_to_user_data(expr->right))
128 return true;
131 return false;
134 bool points_to_user_data(struct expression *expr)
136 struct sm_state *sm;
138 expr = strip_expr(expr);
139 if (!expr)
140 return false;
142 if (expr->type == EXPR_ASSIGNMENT)
143 return points_to_user_data(expr->left);
145 if (is_array_of_user_data(expr))
146 return true;
148 if (is_skb_data(expr))
149 return true;
151 if (is_points_to_user_data_fn(expr))
152 return true;
154 sm = get_sm_state_expr(my_id, expr);
155 if (sm && slist_has_state(sm->possible, &user_data))
156 return true;
157 return false;
160 void set_points_to_user_data(struct expression *expr)
162 set_state_expr(my_id, expr, &user_data);
165 static bool handle_memcpy_fake_assignments(struct expression *expr)
167 struct expression *left = strip_parens(expr->left);
168 struct expression *right = strip_parens(expr->right);
170 /* memcpy(array, src, sizeof(array)) gets turned into *array = *src; */
172 if (left->type != EXPR_PREOP || left->op != '*')
173 return false;
174 if (right->type != EXPR_PREOP || right->op != '*')
175 return false;
176 left = strip_expr(left->unop);
177 right = strip_expr(right->unop);
179 if (points_to_user_data(right)) {
180 set_points_to_user_data(left);
181 return true;
183 return false;
186 static void match_assign(struct expression *expr)
188 if (!is_ptr_type(get_type(expr->left)))
189 return;
191 if (points_to_user_data(expr->right)) {
192 set_points_to_user_data(expr->left);
193 return;
196 if (handle_memcpy_fake_assignments(expr))
197 return;
199 if (get_state_expr(my_id, expr->left))
200 set_state_expr(my_id, expr->left, &undefined);
203 static void match_user_copy(const char *fn, struct expression *expr, void *_unused)
205 struct expression *dest, *size;
206 sval_t sval;
208 dest = get_argument_from_call_expr(expr->args, 0);
209 dest = strip_expr(dest);
210 if (!dest)
211 return;
213 size = get_argument_from_call_expr(expr->args, 2);
214 if (get_implied_value(size, &sval))
215 return;
217 set_state_expr(my_id, dest, &user_data);
220 static void return_info_callback(int return_id, char *return_ranges,
221 struct expression *returned_expr,
222 int param,
223 const char *printed_name,
224 struct sm_state *sm)
226 int type = USER_PTR_SET;
228 if (!slist_has_state(sm->possible, &user_data))
229 return;
231 if (param >= 0) {
232 if (get_state_stree(get_start_states(), my_id, sm->name, sm->sym))
233 return;
234 } else {
235 if (!param_was_set_var_sym(sm->name, sm->sym))
236 type = USER_PTR;
238 if (parent_is_gone_var_sym(sm->name, sm->sym))
239 return;
241 sql_insert_return_states(return_id, return_ranges, type,
242 param, printed_name, "");
245 static void returns_user_ptr_helper(struct expression *expr, int param, char *key, char *value, bool set)
247 struct expression *arg;
248 struct expression *call;
249 char *name;
250 struct symbol *sym;
252 call = expr;
253 while (call->type == EXPR_ASSIGNMENT)
254 call = strip_expr(call->right);
255 if (call->type != EXPR_CALL)
256 return;
258 if (!set && !we_pass_user_data(call))
259 return;
261 if (param == -1) {
262 if (expr->type != EXPR_ASSIGNMENT) {
263 /* Nothing to do. Fake assignments should handle it */
264 return;
266 arg = expr->left;
267 goto set_user;
270 arg = get_argument_from_call_expr(call->args, param);
271 if (!arg)
272 return;
273 set_user:
274 name = get_variable_from_key(arg, key, &sym);
275 if (!name || !sym)
276 goto free;
277 set_state(my_id, name, sym, &user_data);
278 free:
279 free_string(name);
282 static void returns_user_ptr(struct expression *expr, int param, char *key, char *value)
284 returns_user_ptr_helper(expr, param, key, value, false);
287 static void returns_user_ptr_set(struct expression *expr, int param, char *key, char *value)
289 returns_user_ptr_helper(expr, param, key, value, true);
292 static void set_param_user_ptr(const char *name, struct symbol *sym, char *key, char *value)
294 struct expression *expr;
295 char *fullname;
297 expr = symbol_expression(sym);
298 fullname = get_variable_from_key(expr, key, NULL);
299 if (!fullname)
300 return;
301 set_state(my_id, fullname, sym, &user_data);
304 static void caller_info_callback(struct expression *call, int param, char *printed_name, struct sm_state *sm)
306 if (!slist_has_state(sm->possible, &user_data))
307 return;
308 sql_insert_caller_info(call, USER_PTR, param, printed_name, "");
311 void register_points_to_user_data(int id)
313 my_id = id;
315 if (option_project != PROJ_KERNEL)
316 return;
318 add_hook(&match_assign, ASSIGNMENT_HOOK);
320 add_function_hook("copy_from_user", &match_user_copy, NULL);
321 add_function_hook("__copy_from_user", &match_user_copy, NULL);
323 add_caller_info_callback(my_id, caller_info_callback);
324 add_return_info_callback(my_id, return_info_callback);
326 select_caller_info_hook(set_param_user_ptr, USER_PTR);
327 select_return_states_hook(USER_PTR, &returns_user_ptr);
328 select_return_states_hook(USER_PTR_SET, &returns_user_ptr_set);