flow: handle struct initialization better
[smatch.git] / check_user_data.c
blob7284e0bcc0579f1d3adfd1bc000d66ada62c124c
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"
20 static int my_id;
22 STATE(capped);
23 STATE(user_data);
25 int is_user_data(struct expression *expr)
27 struct state_list *slist = NULL;
28 struct sm_state *tmp;
29 struct symbol *sym;
30 char *name;
31 int user = 0;
33 expr = strip_expr(expr);
34 if (!expr)
35 return 0;
36 if (is_capped(expr))
37 return 0;
38 if (expr->type == EXPR_BINOP) {
39 if (is_user_data(expr->left))
40 return 1;
41 if (is_user_data(expr->right))
42 return 1;
43 return 0;
45 if (expr->type == EXPR_PREOP && expr->op == '&')
46 expr = strip_expr(expr->unop);
48 tmp = get_sm_state_expr(my_id, expr);
49 if (tmp)
50 return slist_has_state(tmp->possible, &user_data);
52 name = get_variable_from_expr_complex(expr, &sym);
53 if (!name || !sym)
54 goto free;
56 slist = get_all_states(my_id);
57 FOR_EACH_PTR(slist, tmp) {
58 if (tmp->sym != sym)
59 continue;
60 if (!strncmp(tmp->name, name, strlen(tmp->name))) {
61 if (slist_has_state(tmp->possible, &user_data))
62 user = 1;
63 goto free;
65 } END_FOR_EACH_PTR(tmp);
67 free:
68 free_slist(&slist);
69 free_string(name);
70 return user;
73 void set_param_user_data(const char *name, struct symbol *sym, char *key, char *value)
75 char fullname[256];
77 if (strncmp(key, "$$", 2))
78 return;
79 snprintf(fullname, 256, "%s%s", name, key + 2);
80 set_state(my_id, fullname, sym, &user_data);
83 static void match_condition(struct expression *expr)
85 switch (expr->op) {
86 case '<':
87 case SPECIAL_LTE:
88 case SPECIAL_UNSIGNED_LT:
89 case SPECIAL_UNSIGNED_LTE:
90 if (is_user_data(expr->left))
91 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
92 if (is_user_data(expr->right))
93 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
94 break;
95 case '>':
96 case SPECIAL_GTE:
97 case SPECIAL_UNSIGNED_GT:
98 case SPECIAL_UNSIGNED_GTE:
99 if (is_user_data(expr->right))
100 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
101 if (is_user_data(expr->left))
102 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
103 break;
104 case SPECIAL_EQUAL:
105 if (is_user_data(expr->left))
106 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
107 if (is_user_data(expr->right))
108 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
109 break;
110 case SPECIAL_NOTEQUAL:
111 if (is_user_data(expr->left))
112 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
113 if (is_user_data(expr->right))
114 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
115 break;
116 default:
117 return;
122 static void match_normal_assign(struct expression *expr)
124 if (is_user_data(expr->left))
125 set_state_expr(my_id, expr->left, &capped);
128 static void match_assign(struct expression *expr)
130 char *name;
132 name = get_macro_name(expr->pos);
133 if (!name || strcmp(name, "get_user") != 0) {
134 match_normal_assign(expr);
135 return;
137 name = get_variable_from_expr(expr->right, NULL);
138 if (!name || strcmp(name, "__val_gu") != 0)
139 goto free;
140 set_state_expr(my_id, expr->left, &user_data);
141 free:
142 free_string(name);
145 static void match_user_copy(const char *fn, struct expression *expr, void *_param)
147 int param = PTR_INT(_param);
148 struct expression *dest;
150 dest = get_argument_from_call_expr(expr->args, param);
151 dest = strip_expr(dest);
152 if (!dest)
153 return;
154 /* the first thing I tested this on pass &foo to a function */
155 set_state_expr(my_id, dest, &user_data);
156 if (dest->type == EXPR_PREOP && dest->op == '&') {
157 /* but normally I'd think it would pass the actual variable */
158 dest = dest->unop;
159 set_state_expr(my_id, dest, &user_data);
163 static void match_user_assign_function(const char *fn, struct expression *expr, void *unused)
165 set_state_expr(my_id, expr->left, &user_data);
168 static void match_assign_userdata(struct expression *expr)
170 if (is_user_data(expr->right))
171 set_state_expr(my_id, expr->left, &user_data);
174 static void match_caller_info(struct expression *expr)
176 struct expression *tmp;
177 char *func;
178 int i;
180 func = get_fnptr_name(expr->fn);
181 if (!func)
182 return;
184 i = 0;
185 FOR_EACH_PTR(expr->args, tmp) {
186 if (is_user_data(tmp))
187 sm_msg("info: passes user_data %s %d '$$' %s", func, i,
188 is_static(expr->fn) ? "static" : "global");
189 i++;
190 } END_FOR_EACH_PTR(tmp);
193 static void struct_member_callback(char *fn, char *global_static, int param, char *printed_name, struct smatch_state *state)
195 if (state == &capped)
196 return;
197 sm_msg("info: passes user_data '%s' %d '%s' %s", fn, param, printed_name, global_static);
200 static void match_return(struct expression *expr)
202 if (is_user_data(expr))
203 sm_msg("info: returns_user_data %s", global_static());
206 static int db_user_data;
207 static int db_user_data_callback(void *unused, int argc, char **argv, char **azColName)
209 db_user_data = 1;
210 return 0;
213 static int passes_user_data(struct expression *expr)
215 struct expression *arg;
217 FOR_EACH_PTR(expr->args, arg) {
218 if (is_user_data(arg))
219 return 1;
220 } END_FOR_EACH_PTR(arg);
222 return 0;
225 static void match_call_assignment(struct expression *expr)
227 struct symbol *sym;
228 static char sql_filter[1024];
230 if (expr->right->fn->type != EXPR_SYMBOL)
231 return;
232 sym = expr->right->fn->symbol;
233 if (!sym)
234 return;
236 if (!passes_user_data(expr->right))
237 return;
239 if (sym->ctype.modifiers & MOD_STATIC) {
240 snprintf(sql_filter, 1024, "file = '%s' and function = '%s' and type = %d;",
241 get_filename(), sym->ident->name, USER_DATA);
242 } else {
243 snprintf(sql_filter, 1024, "function = '%s' and static = 0 and type = %d;",
244 sym->ident->name, USER_DATA);
247 db_user_data = 0;
248 run_sql(db_user_data_callback, "select value from return_info where %s",
249 sql_filter);
250 if (db_user_data)
251 set_state_expr(my_id, expr->left, &user_data);
254 void check_user_data(int id)
256 if (option_project != PROJ_KERNEL)
257 return;
258 my_id = id;
259 add_definition_db_callback(set_param_user_data, USER_DATA);
260 add_hook(match_call_assignment, CALL_ASSIGNMENT_HOOK);
261 add_hook(&match_condition, CONDITION_HOOK);
262 add_hook(&match_assign, ASSIGNMENT_HOOK);
263 add_hook(&match_assign_userdata, ASSIGNMENT_HOOK);
264 add_function_hook("copy_from_user", &match_user_copy, INT_PTR(0));
265 add_function_hook("__copy_from_user", &match_user_copy, INT_PTR(0));
266 add_function_hook("memcpy_fromiovec", &match_user_copy, INT_PTR(0));
267 add_function_assign_hook("kmemdup_user", &match_user_assign_function, NULL);
268 if (option_info) {
269 add_hook(&match_caller_info, FUNCTION_CALL_HOOK);
270 add_member_info_callback(my_id, struct_member_callback);
271 add_hook(&match_return, RETURN_HOOK);