user_data: syscalls are a source of user data
[smatch.git] / check_user_data.c
blob25de7a70eb5d6605b462da23c8e410d1408eff9a
1 /*
2 * smatch/check_user_data.c
4 * Copyright (C) 2011 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
11 * There are a couple checks that try to see if a variable
12 * comes from the user. It would be better to unify them
13 * into one place. Also it we should follow the data down
14 * the call paths. Hence this file.
17 #include "smatch.h"
18 #include "smatch_slist.h"
19 #include "smatch_extra.h"
21 static int my_id;
23 STATE(capped);
24 STATE(user_data);
26 static int is_user_macro(struct expression *expr)
28 return 0;
31 static int is_skb_data(struct expression *expr)
33 struct symbol *sym;
34 char *name;
35 int len;
36 int ret = 0;
38 name = get_variable_from_expr(expr, &sym);
39 if (!name || !sym)
40 goto free;
42 sym = get_base_type(sym);
43 if (!sym || sym->type != SYM_PTR)
44 goto free;
45 sym = get_base_type(sym);
46 if (!sym || sym->type != SYM_STRUCT || !sym->ident)
47 goto free;
48 if (strcmp(sym->ident->name, "sk_buff") != 0)
49 goto free;
51 len = strlen(name);
52 if (len < 6)
53 goto free;
54 if (strcmp(name + len - 6, "->data") == 0)
55 ret = 1;
57 free:
58 free_string(name);
59 return ret;
62 int is_user_data(struct expression *expr)
64 struct state_list *slist = NULL;
65 struct sm_state *tmp;
66 struct symbol *sym;
67 char *name;
68 int user = 0;
70 if (!expr)
71 return 0;
73 if (is_capped(expr))
74 return 0;
76 if (is_user_macro(expr))
77 return 1;
78 if (is_skb_data(expr))
79 return 1;
81 expr = strip_expr(expr); /* this has to come after is_user_macro() */
83 if (expr->type == EXPR_BINOP) {
84 if (is_user_data(expr->left))
85 return 1;
86 if (is_user_data(expr->right))
87 return 1;
88 return 0;
90 if (expr->type == EXPR_PREOP && (expr->op == '&' || expr->op == '*'))
91 expr = strip_expr(expr->unop);
93 tmp = get_sm_state_expr(my_id, expr);
94 if (tmp)
95 return slist_has_state(tmp->possible, &user_data);
97 name = get_variable_from_expr_complex(expr, &sym);
98 if (!name || !sym)
99 goto free;
101 slist = get_all_states(my_id);
102 FOR_EACH_PTR(slist, tmp) {
103 if (tmp->sym != sym)
104 continue;
105 if (!strncmp(tmp->name, name, strlen(tmp->name))) {
106 if (slist_has_state(tmp->possible, &user_data))
107 user = 1;
108 goto free;
110 } END_FOR_EACH_PTR(tmp);
112 free:
113 free_slist(&slist);
114 free_string(name);
115 return user;
118 void set_param_user_data(const char *name, struct symbol *sym, char *key, char *value)
120 char fullname[256];
122 if (strncmp(key, "$$", 2))
123 return;
124 snprintf(fullname, 256, "%s%s", name, key + 2);
125 set_state(my_id, fullname, sym, &user_data);
128 static void match_syscall_definition(struct symbol *sym)
130 struct symbol *arg;
131 char *macro;
133 macro = get_macro_name(sym->pos);
134 if (!macro)
135 return;
136 if (strncmp("SYSCALL_DEFINE", macro, strlen("SYSCALL_DEFINE")))
137 return;
139 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
140 set_state(my_id, arg->ident->name, arg, &user_data);
141 } END_FOR_EACH_PTR(arg);
144 static void match_condition(struct expression *expr)
146 switch (expr->op) {
147 case '<':
148 case SPECIAL_LTE:
149 case SPECIAL_UNSIGNED_LT:
150 case SPECIAL_UNSIGNED_LTE:
151 if (is_user_data(expr->left))
152 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
153 if (is_user_data(expr->right))
154 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
155 break;
156 case '>':
157 case SPECIAL_GTE:
158 case SPECIAL_UNSIGNED_GT:
159 case SPECIAL_UNSIGNED_GTE:
160 if (is_user_data(expr->right))
161 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
162 if (is_user_data(expr->left))
163 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
164 break;
165 case SPECIAL_EQUAL:
166 if (is_user_data(expr->left))
167 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
168 if (is_user_data(expr->right))
169 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
170 break;
171 case SPECIAL_NOTEQUAL:
172 if (is_user_data(expr->left))
173 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
174 if (is_user_data(expr->right))
175 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
176 break;
177 default:
178 return;
183 static void match_normal_assign(struct expression *expr)
185 if (is_user_data(expr->left))
186 set_state_expr(my_id, expr->left, &capped);
189 static void match_assign(struct expression *expr)
191 char *name;
193 name = get_macro_name(expr->pos);
194 if (!name || strcmp(name, "get_user") != 0) {
195 match_normal_assign(expr);
196 return;
198 name = get_variable_from_expr(expr->right, NULL);
199 if (!name || strcmp(name, "__val_gu") != 0)
200 goto free;
201 set_state_expr(my_id, expr->left, &user_data);
202 free:
203 free_string(name);
206 static void match_user_copy(const char *fn, struct expression *expr, void *_param)
208 int param = PTR_INT(_param);
209 struct expression *dest;
211 dest = get_argument_from_call_expr(expr->args, param);
212 dest = strip_expr(dest);
213 if (!dest)
214 return;
215 /* the first thing I tested this on pass &foo to a function */
216 set_state_expr(my_id, dest, &user_data);
217 if (dest->type == EXPR_PREOP && dest->op == '&') {
218 /* but normally I'd think it would pass the actual variable */
219 dest = dest->unop;
220 set_state_expr(my_id, dest, &user_data);
224 static void match_user_assign_function(const char *fn, struct expression *expr, void *unused)
226 set_state_expr(my_id, expr->left, &user_data);
229 static void match_assign_userdata(struct expression *expr)
231 if (is_user_data(expr->right))
232 set_state_expr(my_id, expr->left, &user_data);
235 static void match_caller_info(struct expression *expr)
237 struct expression *tmp;
238 char *func;
239 int i;
241 func = get_fnptr_name(expr->fn);
242 if (!func)
243 return;
245 i = 0;
246 FOR_EACH_PTR(expr->args, tmp) {
247 if (is_user_data(tmp))
248 sm_msg("info: passes user_data %s %d '$$' %s", func, i,
249 is_static(expr->fn) ? "static" : "global");
250 i++;
251 } END_FOR_EACH_PTR(tmp);
254 static void struct_member_callback(char *fn, char *global_static, int param, char *printed_name, struct smatch_state *state)
256 if (state == &capped)
257 return;
258 sm_msg("info: passes user_data '%s' %d '%s' %s", fn, param, printed_name, global_static);
261 static void match_return(struct expression *expr)
263 if (is_user_data(expr))
264 sm_msg("info: returns_user_data %s", global_static());
267 static int db_user_data;
268 static int db_user_data_callback(void *unused, int argc, char **argv, char **azColName)
270 db_user_data = 1;
271 return 0;
274 static int passes_user_data(struct expression *expr)
276 struct expression *arg;
278 FOR_EACH_PTR(expr->args, arg) {
279 if (is_user_data(arg))
280 return 1;
281 } END_FOR_EACH_PTR(arg);
283 return 0;
286 static void match_call_assignment(struct expression *expr)
288 struct symbol *sym;
289 static char sql_filter[1024];
291 if (expr->right->fn->type != EXPR_SYMBOL)
292 return;
293 sym = expr->right->fn->symbol;
294 if (!sym)
295 return;
297 if (!passes_user_data(expr->right))
298 return;
300 if (sym->ctype.modifiers & MOD_STATIC) {
301 snprintf(sql_filter, 1024, "file = '%s' and function = '%s' and type = %d;",
302 get_filename(), sym->ident->name, USER_DATA);
303 } else {
304 snprintf(sql_filter, 1024, "function = '%s' and static = 0 and type = %d;",
305 sym->ident->name, USER_DATA);
308 db_user_data = 0;
309 run_sql(db_user_data_callback, "select value from return_info where %s",
310 sql_filter);
311 if (db_user_data)
312 set_state_expr(my_id, expr->left, &user_data);
315 void check_user_data(int id)
317 if (option_project != PROJ_KERNEL)
318 return;
319 my_id = id;
320 add_definition_db_callback(set_param_user_data, USER_DATA);
321 add_hook(&match_syscall_definition, FUNC_DEF_HOOK);
322 add_hook(match_call_assignment, CALL_ASSIGNMENT_HOOK);
323 add_hook(&match_condition, CONDITION_HOOK);
324 add_hook(&match_assign, ASSIGNMENT_HOOK);
325 add_hook(&match_assign_userdata, ASSIGNMENT_HOOK);
326 add_function_hook("copy_from_user", &match_user_copy, INT_PTR(0));
327 add_function_hook("__copy_from_user", &match_user_copy, INT_PTR(0));
328 add_function_hook("memcpy_fromiovec", &match_user_copy, INT_PTR(0));
329 add_function_assign_hook("kmemdup_user", &match_user_assign_function, NULL);
330 if (option_info) {
331 add_hook(&match_caller_info, FUNCTION_CALL_HOOK);
332 add_member_info_callback(my_id, struct_member_callback);
333 add_hook(&match_return, RETURN_HOOK);