db: caller info needs to record the -1 parameters
[smatch.git] / check_user_data.c
blobaafcce9ede74ddee5325b399c3bb2da61644cd05
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 (is_capped(expr))
35 return 0;
36 if (expr->type == EXPR_BINOP) {
37 if (is_user_data(expr->left))
38 return 1;
39 if (is_user_data(expr->right))
40 return 1;
41 return 0;
43 if (expr->type == EXPR_PREOP && expr->op == '&')
44 expr = strip_expr(expr->unop);
46 tmp = get_sm_state_expr(my_id, expr);
47 if (tmp)
48 return slist_has_state(tmp->possible, &user_data);
50 name = get_variable_from_expr_complex(expr, &sym);
51 if (!name || !sym)
52 goto free;
54 slist = get_all_states(my_id);
55 FOR_EACH_PTR(slist, tmp) {
56 if (tmp->sym != sym)
57 continue;
58 if (!strncmp(tmp->name, name, strlen(tmp->name))) {
59 if (slist_has_state(tmp->possible, &user_data))
60 user = 1;
61 goto free;
63 } END_FOR_EACH_PTR(tmp);
65 free:
66 free_slist(&slist);
67 free_string(name);
68 return user;
71 void set_param_user_data(const char *name, struct symbol *sym, char *key, char *value)
73 char fullname[256];
75 if (strncmp(key, "$$", 2))
76 return;
77 snprintf(fullname, 256, "%s%s", name, key + 2);
78 set_state(my_id, fullname, sym, &user_data);
81 static void match_condition(struct expression *expr)
83 switch (expr->op) {
84 case '<':
85 case SPECIAL_LTE:
86 case SPECIAL_UNSIGNED_LT:
87 case SPECIAL_UNSIGNED_LTE:
88 if (is_user_data(expr->left))
89 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
90 if (is_user_data(expr->right))
91 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
92 break;
93 case '>':
94 case SPECIAL_GTE:
95 case SPECIAL_UNSIGNED_GT:
96 case SPECIAL_UNSIGNED_GTE:
97 if (is_user_data(expr->right))
98 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
99 if (is_user_data(expr->left))
100 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
101 break;
102 case SPECIAL_EQUAL:
103 if (is_user_data(expr->left))
104 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
105 if (is_user_data(expr->right))
106 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
107 break;
108 case SPECIAL_NOTEQUAL:
109 if (is_user_data(expr->left))
110 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
111 if (is_user_data(expr->right))
112 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
113 break;
114 default:
115 return;
120 static void match_normal_assign(struct expression *expr)
122 if (is_user_data(expr->left))
123 set_state_expr(my_id, expr->left, &capped);
126 static void match_assign(struct expression *expr)
128 char *name;
130 name = get_macro_name(&expr->pos);
131 if (!name || strcmp(name, "get_user") != 0) {
132 match_normal_assign(expr);
133 return;
135 name = get_variable_from_expr(expr->right, NULL);
136 if (!name || strcmp(name, "__val_gu") != 0)
137 goto free;
138 set_state_expr(my_id, expr->left, &user_data);
139 free:
140 free_string(name);
143 static void match_user_copy(const char *fn, struct expression *expr, void *_param)
145 int param = PTR_INT(_param);
146 struct expression *dest;
148 dest = get_argument_from_call_expr(expr->args, param);
149 dest = strip_expr(dest);
150 if (!dest)
151 return;
152 /* the first thing I tested this on pass &foo to a function */
153 set_state_expr(my_id, dest, &user_data);
154 if (dest->type == EXPR_PREOP && dest->op == '&') {
155 /* but normally I'd think it would pass the actual variable */
156 dest = dest->unop;
157 set_state_expr(my_id, dest, &user_data);
161 static void match_user_assign_function(const char *fn, struct expression *expr, void *unused)
163 set_state_expr(my_id, expr->left, &user_data);
166 static void match_assign_userdata(struct expression *expr)
168 if (is_user_data(expr->right))
169 set_state_expr(my_id, expr->left, &user_data);
172 static void match_caller_info(struct expression *expr)
174 struct expression *tmp;
175 char *func;
176 int i;
178 func = get_fnptr_name(expr->fn);
179 if (!func)
180 return;
182 i = 0;
183 FOR_EACH_PTR(expr->args, tmp) {
184 if (is_user_data(tmp))
185 sm_msg("info: passes user_data %s %d '$$'", func, i);
186 i++;
187 } END_FOR_EACH_PTR(tmp);
190 static void struct_member_callback(char *fn, int param, char *printed_name, struct smatch_state *state)
192 if (state == &capped)
193 return;
194 sm_msg("info: passes user_data '%s' %d '%s'", fn, param, printed_name);
197 void check_user_data(int id)
199 if (option_project != PROJ_KERNEL)
200 return;
201 my_id = id;
202 add_definition_db_callback(set_param_user_data, USER_DATA);
203 add_hook(&match_condition, CONDITION_HOOK);
204 add_hook(&match_assign, ASSIGNMENT_HOOK);
205 add_hook(&match_assign_userdata, ASSIGNMENT_HOOK);
206 add_function_hook("copy_from_user", &match_user_copy, INT_PTR(0));
207 add_function_hook("__copy_from_user", &match_user_copy, INT_PTR(0));
208 add_function_hook("memcpy_fromiovec", &match_user_copy, INT_PTR(0));
209 add_function_assign_hook("kmemdup_user", &match_user_assign_function, NULL);
210 if (option_info) {
211 add_hook(&match_caller_info, FUNCTION_CALL_HOOK);
212 add_member_info_callback(my_id, struct_member_callback);