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"
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
)
58 expr
= strip_expr(expr
);
62 if (expr
->type
== EXPR_BINOP
&& expr
->op
== '+')
63 return is_skb_data(expr
->left
);
65 expr
= strip_expr(expr
);
68 if (expr
->type
!= EXPR_DEREF
|| expr
->op
!= '.')
73 if (strcmp(expr
->member
->name
, "data") != 0)
76 sym
= expr_to_sym(expr
->deref
);
79 sym
= get_real_base_type(sym
);
80 if (!sym
|| sym
->type
!= SYM_PTR
)
82 sym
= get_real_base_type(sym
);
83 if (!sym
|| sym
->type
!= SYM_STRUCT
|| !sym
->ident
)
85 if (strcmp(sym
->ident
->name
, "sk_buff") != 0)
91 bool is_user_data_fn(struct symbol
*fn
)
95 if (!fn
|| !fn
->ident
)
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;
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
||
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
))
127 if (points_to_user_data(expr
->right
))
134 bool points_to_user_data(struct expression
*expr
)
138 expr
= strip_expr(expr
);
142 if (expr
->type
== EXPR_ASSIGNMENT
)
143 return points_to_user_data(expr
->left
);
145 if (is_array_of_user_data(expr
))
148 if (is_skb_data(expr
))
151 if (is_points_to_user_data_fn(expr
))
154 sm
= get_sm_state_expr(my_id
, expr
);
155 if (sm
&& slist_has_state(sm
->possible
, &user_data
))
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
!= '*')
174 if (right
->type
!= EXPR_PREOP
|| right
->op
!= '*')
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
);
186 static void match_assign(struct expression
*expr
)
188 if (!is_ptr_type(get_type(expr
->left
)))
191 if (points_to_user_data(expr
->right
)) {
192 set_points_to_user_data(expr
->left
);
196 if (handle_memcpy_fake_assignments(expr
))
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
;
208 dest
= get_argument_from_call_expr(expr
->args
, 0);
209 dest
= strip_expr(dest
);
213 size
= get_argument_from_call_expr(expr
->args
, 2);
214 if (get_implied_value(size
, &sval
))
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
,
223 const char *printed_name
,
226 int type
= USER_PTR_SET
;
228 if (!slist_has_state(sm
->possible
, &user_data
))
232 if (get_state_stree(get_start_states(), my_id
, sm
->name
, sm
->sym
))
235 if (!param_was_set_var_sym(sm
->name
, sm
->sym
))
238 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
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
;
253 while (call
->type
== EXPR_ASSIGNMENT
)
254 call
= strip_expr(call
->right
);
255 if (call
->type
!= EXPR_CALL
)
258 if (!set
&& !we_pass_user_data(call
))
262 if (expr
->type
!= EXPR_ASSIGNMENT
) {
263 /* Nothing to do. Fake assignments should handle it */
270 arg
= get_argument_from_call_expr(call
->args
, param
);
274 name
= get_variable_from_key(arg
, key
, &sym
);
277 set_state(my_id
, name
, sym
, &user_data
);
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
;
297 expr
= symbol_expression(sym
);
298 fullname
= get_variable_from_key(expr
, key
, NULL
);
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
))
308 sql_insert_caller_info(call
, USER_PTR
, param
, printed_name
, "");
311 void register_points_to_user_data(int id
)
315 if (option_project
!= PROJ_KERNEL
)
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
);