*new* smatch_capped: introduce is_capped() function
[smatch.git] / check_user_data.c
blob39ec65560d9dedb7996a994df94a2697f164ccbe
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->type == EXPR_PREOP && expr->op == '&')
35 expr = strip_expr(expr->unop);
37 tmp = get_sm_state_expr(my_id, expr);
38 if (tmp)
39 return slist_has_state(tmp->possible, &user_data);
41 name = get_variable_from_expr_complex(expr, &sym);
42 if (!name || !sym)
43 goto free;
45 slist = get_all_states(my_id);
46 FOR_EACH_PTR(slist, tmp) {
47 if (tmp->sym != sym)
48 continue;
49 if (!strncmp(tmp->name, name, strlen(tmp->name))) {
50 if (slist_has_state(tmp->possible, &user_data))
51 user = 1;
52 goto free;
54 } END_FOR_EACH_PTR(tmp);
56 free:
57 free_slist(&slist);
58 free_string(name);
59 return user;
62 void set_param_user_data(const char *name, struct symbol *sym, char *key, char *value)
64 char fullname[256];
66 if (strncmp(key, "$$", 2))
67 return;
68 snprintf(fullname, 256, "%s%s", name, key + 2);
69 set_state(my_id, fullname, sym, &user_data);
72 static void match_condition(struct expression *expr)
74 switch (expr->op) {
75 case '<':
76 case SPECIAL_LTE:
77 case SPECIAL_UNSIGNED_LT:
78 case SPECIAL_UNSIGNED_LTE:
79 if (is_user_data(expr->left))
80 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
81 if (is_user_data(expr->right))
82 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
83 break;
84 case '>':
85 case SPECIAL_GTE:
86 case SPECIAL_UNSIGNED_GT:
87 case SPECIAL_UNSIGNED_GTE:
88 if (is_user_data(expr->right))
89 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
90 if (is_user_data(expr->left))
91 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
92 break;
93 case SPECIAL_EQUAL:
94 if (is_user_data(expr->left))
95 set_true_false_states_expr(my_id, expr->left, &capped, NULL);
96 if (is_user_data(expr->right))
97 set_true_false_states_expr(my_id, expr->right, &capped, NULL);
98 break;
99 case SPECIAL_NOTEQUAL:
100 if (is_user_data(expr->left))
101 set_true_false_states_expr(my_id, expr->left, NULL, &capped);
102 if (is_user_data(expr->right))
103 set_true_false_states_expr(my_id, expr->right, NULL, &capped);
104 break;
105 default:
106 return;
111 static void match_normal_assign(struct expression *expr)
113 if (is_user_data(expr->left))
114 set_state_expr(my_id, expr->left, &capped);
117 static void match_assign(struct expression *expr)
119 char *name;
121 name = get_macro_name(&expr->pos);
122 if (!name || strcmp(name, "get_user") != 0) {
123 match_normal_assign(expr);
124 return;
126 name = get_variable_from_expr(expr->right, NULL);
127 if (!name || strcmp(name, "__val_gu") != 0)
128 goto free;
129 set_state_expr(my_id, expr->left, &user_data);
130 free:
131 free_string(name);
134 static void match_user_copy(const char *fn, struct expression *expr, void *_param)
136 int param = PTR_INT(_param);
137 struct expression *dest;
139 dest = get_argument_from_call_expr(expr->args, param);
140 dest = strip_expr(dest);
141 if (!dest)
142 return;
143 /* the first thing I tested this on pass &foo to a function */
144 set_state_expr(my_id, dest, &user_data);
145 if (dest->type == EXPR_PREOP && dest->op == '&') {
146 /* but normally I'd think it would pass the actual variable */
147 dest = dest->unop;
148 set_state_expr(my_id, dest, &user_data);
152 static void match_user_assign_function(const char *fn, struct expression *expr, void *unused)
154 set_state_expr(my_id, expr->left, &user_data);
157 static void match_assign_userdata(struct expression *expr)
159 if (is_user_data(expr->right))
160 set_state_expr(my_id, expr->left, &user_data);
163 static void match_caller_info(struct expression *expr)
165 struct expression *tmp;
166 char *func;
167 int i;
169 func = get_fnptr_name(expr->fn);
170 if (!func)
171 return;
173 i = 0;
174 FOR_EACH_PTR(expr->args, tmp) {
175 if (is_user_data(tmp))
176 sm_msg("info: passes user_data %s %d '$$'", func, i);
177 i++;
178 } END_FOR_EACH_PTR(tmp);
181 static void struct_member_callback(char *fn, int param, char *printed_name, struct smatch_state *state)
183 if (state == &capped)
184 return;
185 sm_msg("info: passes user_data '%s' %d '%s'", fn, param, printed_name);
188 void check_user_data(int id)
190 if (option_project != PROJ_KERNEL)
191 return;
192 my_id = id;
193 add_definition_db_callback(set_param_user_data, USER_DATA);
194 add_hook(&match_condition, CONDITION_HOOK);
195 add_hook(&match_assign, ASSIGNMENT_HOOK);
196 add_hook(&match_assign_userdata, ASSIGNMENT_HOOK);
197 add_function_hook("copy_from_user", &match_user_copy, INT_PTR(0));
198 add_function_hook("__copy_from_user", &match_user_copy, INT_PTR(0));
199 add_function_hook("memcpy_fromiovec", &match_user_copy, INT_PTR(0));
200 add_function_assign_hook("kmemdup_user", &match_user_assign_function, NULL);
201 if (option_info) {
202 add_hook(&match_caller_info, FUNCTION_CALL_HOOK);
203 add_member_info_callback(my_id, struct_member_callback);