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:
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
28 * So now we've separated out the stuff that points to a user_buf from the other
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.
42 #include "smatch_slist.h"
43 #include "smatch_extra.h"
56 static struct user_fn_info func_table
[] = {
57 { "(struct ksmbd_transport_ops)->read", USER_DATA
, 1, "$" },
60 static struct user_fn_info call_table
[] = {
61 { "__handle_ksmbd_work", USER_DATA
, 0, "$->request_buf" },
64 static const char *returns_pointer_to_user_data
[] = {
65 "nlmsg_data", "nla_data", "memdup_user", "kmap_atomic", "skb_network_header",
66 "cfg80211_find_elem_match", "ieee80211_bss_get_elem", "cfg80211_find_elem",
67 "ieee80211_bss_get_ie",
70 bool is_skb_data(struct expression
*expr
)
74 expr
= strip_expr(expr
);
77 if (expr
->type
!= EXPR_DEREF
)
82 if (strcmp(expr
->member
->name
, "data") != 0)
85 sym
= get_type(expr
->deref
);
88 if (sym
->type
== SYM_PTR
)
89 sym
= get_real_base_type(sym
);
90 if (!sym
|| sym
->type
!= SYM_STRUCT
|| !sym
->ident
)
92 if (strcmp(sym
->ident
->name
, "sk_buff") != 0)
98 bool is_user_data_fn(struct symbol
*fn
)
102 if (!fn
|| !fn
->ident
)
105 for (i
= 0; i
< ARRAY_SIZE(returns_pointer_to_user_data
); i
++) {
106 if (strcmp(fn
->ident
->name
, returns_pointer_to_user_data
[i
]) == 0) {
107 // func_gets_user_data = true;
114 static struct expression
*returns_user_data_fn
;
115 static bool is_points_to_user_data_fn(struct expression
*expr
)
117 expr
= strip_expr(expr
);
118 if (returns_user_data_fn
&& expr
== returns_user_data_fn
)
120 if (!expr
|| expr
->type
!= EXPR_CALL
|| expr
->fn
->type
!= EXPR_SYMBOL
||
123 return is_user_data_fn(expr
->fn
->symbol
);
126 static bool is_array_of_user_data(struct expression
*expr
)
128 struct expression
*deref
;
131 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '&') {
132 expr
= strip_expr(expr
->unop
);
133 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
134 expr
= strip_expr(expr
->unop
);
137 /* This is for array elements &foo->data[4] */
138 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+') {
139 if (points_to_user_data(expr
->left
))
141 if (points_to_user_data(expr
->right
))
145 /* This is for if you have: foo = skb->data; frob(foo->array); */
146 type
= get_type(expr
);
147 if (!type
|| type
->type
!= SYM_ARRAY
)
150 if (expr
->type
!= EXPR_DEREF
)
152 deref
= strip_expr(expr
->deref
);
153 if (deref
->type
!= EXPR_PREOP
|| deref
->op
!= '*')
155 deref
= strip_expr(deref
->unop
);
156 return points_to_user_data(deref
);
159 static struct expression
*remove_addr_stuff(struct expression
*expr
)
161 /* take "&foo->bar" and return "foo" */
162 expr
= strip_expr(expr
);
163 if (expr
->type
!= EXPR_PREOP
|| expr
->op
!= '&')
165 expr
= strip_expr(expr
->unop
);
166 while (expr
&& expr
->type
== EXPR_DEREF
) {
167 expr
= strip_expr(expr
->deref
);
173 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
174 expr
= strip_expr(expr
->unop
);
178 static bool math_points_to_user_data(struct expression
*expr
)
182 // TODO: is_array_of_user_data() should probably be handled here
184 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+')
185 return math_points_to_user_data(expr
->left
);
187 expr
= remove_addr_stuff(expr
);
189 sm
= get_sm_state_expr(my_id
, expr
);
192 if (slist_has_state(sm
->possible
, &user_data
) ||
193 slist_has_state(sm
->possible
, &user_data_set
))
198 bool points_to_user_data(struct expression
*expr
)
202 expr
= strip_expr(expr
);
206 if (expr
->type
== EXPR_POSTOP
)
207 expr
= strip_expr(expr
->unop
);
209 if (is_fake_call(expr
))
212 if (expr
->type
== EXPR_ASSIGNMENT
)
213 return points_to_user_data(expr
->left
);
215 if (is_array_of_user_data(expr
))
218 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+')
219 return math_points_to_user_data(expr
);
221 if (is_skb_data(expr
))
224 if (is_points_to_user_data_fn(expr
))
227 // FIXME if you have a struct pointer p then p->foo should be handled
228 // by smatch_kernel_user_data.c but if you have (p + 1)->foo then this
229 // should be handled here.
230 sm
= get_sm_state_expr(my_id
, expr
);
233 if (slist_has_state(sm
->possible
, &user_data
) ||
234 slist_has_state(sm
->possible
, &user_data_set
))
239 void set_points_to_user_data(struct expression
*expr
, bool is_new
)
241 struct expression
*tmp
;
243 tmp
= get_assigned_expr(expr
);
245 set_state_expr(my_id
, tmp
, is_new
? &user_data_set
: &user_data
);
246 set_state_expr(my_id
, expr
, is_new
? &user_data_set
: &user_data
);
249 static void match_assign(struct expression
*expr
)
251 if (is_fake_call(expr
->right
))
254 if (!is_ptr_type(get_type(expr
->left
)))
257 if (points_to_user_data(expr
->right
)) {
258 set_state_expr(my_id
, expr
->left
, &user_data
);
262 if (get_state_expr(my_id
, expr
->left
))
263 set_state_expr(my_id
, expr
->left
, &undefined
);
266 static void match_memcpy(const char *fn
, struct expression
*expr
, void *_unused
)
268 struct expression
*dest
, *src
;
270 dest
= get_argument_from_call_expr(expr
->args
, 0);
271 src
= get_argument_from_call_expr(expr
->args
, 1);
273 if (points_to_user_data(src
)) {
274 set_state_expr(my_id
, expr
->left
, &user_data_set
);
278 if (get_state_expr(my_id
, dest
))
279 set_state_expr(my_id
, dest
, &undefined
);
282 static void match_user_copy(const char *fn
, struct expression
*expr
, void *_unused
)
284 struct expression
*dest
, *size
;
287 dest
= get_argument_from_call_expr(expr
->args
, 0);
288 dest
= strip_expr(dest
);
292 size
= get_argument_from_call_expr(expr
->args
, 2);
293 if (get_implied_value(size
, &sval
))
296 set_state_expr(my_id
, dest
, &user_data_set
);
299 static void return_info_callback(int return_id
, char *return_ranges
,
300 struct expression
*returned_expr
,
302 const char *printed_name
,
307 if (strncmp(printed_name
, "&$", 2) == 0)
310 if (is_socket_stuff(sm
->sym
))
314 if (!slist_has_state(sm
->possible
, &user_data_set
))
318 if (slist_has_state(sm
->possible
, &user_data_set
))
320 else if (slist_has_state(sm
->possible
, &user_data
))
325 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
328 sql_insert_return_states(return_id
, return_ranges
, type
,
329 param
, printed_name
, "");
332 static void returns_user_ptr_helper(struct expression
*expr
, int param
, char *key
, char *value
, bool set
)
334 struct expression
*arg
;
335 struct expression
*call
;
340 while (call
->type
== EXPR_ASSIGNMENT
)
341 call
= strip_expr(call
->right
);
342 if (call
->type
!= EXPR_CALL
)
345 if (!set
&& !we_pass_user_data(call
))
349 if (expr
->type
!= EXPR_ASSIGNMENT
) {
350 /* Nothing to do. Fake assignments should handle it */
357 arg
= get_argument_from_call_expr(call
->args
, param
);
361 name
= get_variable_from_key(arg
, key
, &sym
);
364 if (param
== -1 && strcmp(key
, "$") == 0)
365 returns_user_data_fn
= call
;
367 set_state(my_id
, name
, sym
, &user_data_set
);
369 set_state(my_id
, name
, sym
, &user_data
);
374 static void returns_user_ptr(struct expression
*expr
, int param
, char *key
, char *value
)
376 returns_user_ptr_helper(expr
, param
, key
, value
, false);
379 static void returns_user_ptr_set(struct expression
*expr
, int param
, char *key
, char *value
)
381 returns_user_ptr_helper(expr
, param
, key
, value
, true);
384 static void set_param_user_ptr(const char *name
, struct symbol
*sym
, char *key
, char *value
)
386 struct expression
*expr
;
389 expr
= symbol_expression(sym
);
390 fullname
= get_variable_from_key(expr
, key
, NULL
);
393 set_state(my_id
, fullname
, sym
, &user_data
);
396 static void set_param_key_user_ptr_set(struct expression
*expr
, const char *name
,
397 struct symbol
*sym
, void *data
)
399 set_state(my_id
, name
, sym
, &user_data_set
);
402 static void set_param_key_user_ptr(struct expression
*expr
, const char *name
,
403 struct symbol
*sym
, void *data
)
405 set_state(my_id
, name
, sym
, &user_data
);
408 static void caller_info_callback(struct expression
*call
, int param
, char *printed_name
, struct sm_state
*sm
)
410 if (is_socket_stuff(sm
->sym
))
413 if (!slist_has_state(sm
->possible
, &user_data
) &&
414 !slist_has_state(sm
->possible
, &user_data_set
))
417 sql_insert_caller_info(call
, USER_PTR
, param
, printed_name
, "");
420 void register_points_to_user_data(int id
)
422 struct user_fn_info
*info
;
427 if (option_project
!= PROJ_KERNEL
)
430 add_function_data((unsigned long *)&returns_user_data_fn
);
431 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
433 add_function_hook("copy_from_user", &match_user_copy
, NULL
);
434 add_function_hook("memcpy_from_msg", &match_user_copy
, NULL
);
435 add_function_hook("__copy_from_user", &match_user_copy
, NULL
);
437 add_function_hook("memcpy", &match_memcpy
, NULL
);
438 add_function_hook("__memcpy", &match_memcpy
, NULL
);
440 add_caller_info_callback(my_id
, caller_info_callback
);
441 add_return_info_callback(my_id
, return_info_callback
);
443 select_caller_info_hook(set_param_user_ptr
, USER_PTR
);
444 select_return_states_hook(USER_PTR
, &returns_user_ptr
);
445 select_return_states_hook(USER_PTR_SET
, &returns_user_ptr_set
);
447 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
448 info
= &func_table
[i
];
449 add_function_param_key_hook_late(info
->name
, &set_param_key_user_ptr_set
,
450 info
->param
, info
->key
, info
);
453 for (i
= 0; i
< ARRAY_SIZE(call_table
); i
++) {
454 info
= &call_table
[i
];
455 add_function_param_key_hook_early(info
->name
, &set_param_key_user_ptr
,
456 info
->param
, info
->key
, info
);