kvmalloc_NOFS: warn about passing bogus GFP_ arguments to kvmalloc()
[smatch.git] / smatch_param_set.c
blob41f3ba0fd08346da3afbd281e8f7068316533a7d
1 /*
2 * Copyright (C) 2012 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 * This is for functions like:
21 * int foo(int *x)
22 * {
23 * if (*x == 42) {
24 * *x = 0;
25 * return 1;
26 * }
27 * return 0;
28 * }
30 * If we return 1 that means the value of *x has been set to 0. If we return
31 * 0 then we have left *x alone.
35 #include "scope.h"
36 #include "smatch.h"
37 #include "smatch_slist.h"
38 #include "smatch_extra.h"
40 static int my_id;
42 static struct smatch_state *unmatched_state(struct sm_state *sm)
44 return alloc_estate_empty();
47 static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state)
49 struct expression *faked;
50 char *left_name;
51 int ret = 0;
52 int len;
54 if (!__in_fake_assign)
55 return 0;
56 if (!is_whole_rl(estate_rl(state)))
57 return 0;
58 if (get_state(my_id, name, sym))
59 return 0;
61 faked = get_faked_expression();
62 if (!faked)
63 return 0;
64 if ((faked->type == EXPR_PREOP || faked->type == EXPR_POSTOP) &&
65 (faked->op == SPECIAL_INCREMENT || faked->op == SPECIAL_DECREMENT)) {
66 faked = strip_expr(faked->unop);
67 if (faked->type == EXPR_SYMBOL)
68 return 1;
69 return 0;
71 if (faked->type != EXPR_ASSIGNMENT)
72 return 0;
74 left_name = expr_to_var(faked->left);
75 if (!left_name)
76 return 0;
78 len = strlen(left_name);
79 if (strncmp(name, left_name, len) == 0 && name[len] == '-')
80 ret = 1;
81 free_string(left_name);
83 return ret;
86 static bool is_probably_worthless(struct expression *expr)
88 struct expression *faked;
90 if (!__in_fake_struct_assign)
91 return false;
93 faked = get_faked_expression();
94 if (!faked || faked->type != EXPR_ASSIGNMENT)
95 return false;
97 if (faked->left->type == EXPR_PREOP &&
98 faked->left->op == '*')
99 return false;
101 return true;
104 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
106 struct symbol *param_sym;
107 struct symbol *type;
108 char *param_name;
110 if (expr && expr->smatch_flags & Fake)
111 return;
113 if (is_probably_worthless(expr))
114 return;
116 type = get_type(expr);
117 if (type && (type->type == SYM_STRUCT || type->type == SYM_UNION))
118 return;
120 param_name = get_param_var_sym_var_sym(name, sym, NULL, &param_sym);
121 if (!param_name || !param_sym)
122 goto free;
123 if (get_param_num_from_sym(param_sym) < 0)
124 goto free;
125 if (parent_is_set(param_name, param_sym, state))
126 return;
128 set_state(my_id, param_name, param_sym, state);
129 free:
130 free_string(param_name);
134 * This function is is a dirty hack because extra_mod_hook is giving us a NULL
135 * sym instead of a vsl.
137 static void match_array_assignment(struct expression *expr)
139 struct expression *array, *offset;
140 char *name;
141 struct symbol *sym;
142 struct range_list *rl;
143 sval_t sval;
144 char buf[256];
146 if (__in_fake_assign)
147 return;
149 if (!is_array(expr->left))
150 return;
151 array = get_array_base(expr->left);
152 offset = get_array_offset(expr->left);
154 /* These are handled by extra_mod_hook() */
155 if (get_value(offset, &sval))
156 return;
157 name = expr_to_var_sym(array, &sym);
158 if (!name || !sym)
159 goto free;
160 if (map_to_param(name, sym) < 0)
161 goto free;
162 get_absolute_rl(expr->right, &rl);
163 rl = cast_rl(get_type(expr->left), rl);
165 snprintf(buf, sizeof(buf), "*%s", name);
166 set_state(my_id, buf, sym, alloc_estate_rl(rl));
167 free:
168 free_string(name);
171 static char *get_two_dots(const char *name)
173 static char buf[80];
174 int i, cnt = 0;
176 for (i = 0; i < sizeof(buf); i++) {
177 if (name[i] == '.') {
178 cnt++;
179 if (cnt >= 2) {
180 buf[i] = '\0';
181 return buf;
184 buf[i] = name[i];
186 return NULL;
190 * This relies on the fact that these states are stored so that
191 * foo->bar is before foo->bar->baz.
193 static int parent_set(struct string_list *list, const char *name)
195 char *tmp;
196 int len;
197 int ret;
199 FOR_EACH_PTR(list, tmp) {
200 len = strlen(tmp);
201 ret = strncmp(tmp, name, len);
202 if (ret < 0)
203 continue;
204 if (ret > 0)
205 return 0;
206 if (name[len] == '-')
207 return 1;
208 } END_FOR_EACH_PTR(tmp);
210 return 0;
213 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit)
215 struct sm_state *sm;
216 struct smatch_state *extra;
217 int param;
218 struct range_list *rl;
219 const char *param_name;
220 struct string_list *set_list = NULL;
221 char *math_str;
222 char buf[256];
223 char two_dot[80] = "";
224 int count = 0;
226 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
227 if (!estate_rl(sm->state))
228 continue;
229 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
230 if (extra) {
231 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
232 if (!rl)
233 continue;
234 } else {
235 rl = estate_rl(sm->state);
238 param = get_param_key_from_sm(sm, NULL, &param_name);
239 if (param < 0 || !param_name)
240 continue;
241 if (param_name[0] == '&')
242 continue;
243 if (strcmp(param_name, "$") == 0 ||
244 is_recursive_member(param_name) ||
245 is_ignored_kernel_data(param_name)) {
246 insert_string(&set_list, (char *)sm->name);
247 continue;
249 if (limit) {
250 char *new = get_two_dots(param_name);
252 /* no useful information here. */
253 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
254 continue;
256 if (new) {
257 if (strcmp(new, two_dot) == 0)
258 continue;
260 strncpy(two_dot, new, sizeof(two_dot));
261 insert_string(&set_list, (char *)sm->name);
262 sql_insert_return_states(return_id, return_ranges,
263 PARAM_SET, param, new, "s64min-s64max");
264 continue;
268 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
269 if (math_str) {
270 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
271 insert_string(&set_list, (char *)sm->name);
272 sql_insert_return_states(return_id, return_ranges,
273 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
274 param, param_name, buf);
275 continue;
278 /* no useful information here. */
279 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
280 continue;
281 if (is_whole_rl(rl) && parent_was_PARAM_CLEAR(sm->name, sm->sym))
282 continue;
283 if (rl_is_zero(rl) && parent_was_PARAM_CLEAR_ZERO(sm->name, sm->sym))
284 continue;
286 insert_string(&set_list, (char *)sm->name);
288 sql_insert_return_states(return_id, return_ranges,
289 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
290 param, param_name, show_rl(rl));
291 if (limit && ++count > limit)
292 break;
294 } END_FOR_EACH_SM(sm);
296 free_ptr_list((struct ptr_list **)&set_list);
299 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
301 print_return_value_param_helper(return_id, return_ranges, expr, 0);
304 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
306 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
309 static int possibly_empty(struct sm_state *sm)
311 struct sm_state *tmp;
313 FOR_EACH_PTR(sm->possible, tmp) {
314 if (strcmp(tmp->name, "") == 0)
315 return 1;
316 } END_FOR_EACH_PTR(tmp);
317 return 0;
320 int param_was_set_var_sym(const char *name, struct symbol *sym)
322 struct sm_state *sm;
323 char buf[80];
324 int len, i;
326 if (!name)
327 return 0;
329 if (name[0] == '&')
330 name++;
332 len = strlen(name);
333 if (len >= sizeof(buf))
334 len = sizeof(buf) - 1;
336 for (i = 0; i <= len; i++) {
337 if (name[i] != '-' && name[i] != '\0')
338 continue;
340 memcpy(buf, name, i);
341 buf[i] = '\0';
343 sm = get_sm_state(my_id, buf, sym);
344 if (!sm)
345 continue;
346 if (possibly_empty(sm))
347 continue;
348 return 1;
351 if (name[0] == '*')
352 return param_was_set_var_sym(name + 1, sym);
354 return 0;
357 int param_was_set(struct expression *expr)
359 char *name;
360 struct symbol *sym;
361 int ret = 0;
363 name = expr_to_var_sym(expr, &sym);
364 if (!name || !sym)
365 goto free;
367 ret = param_was_set_var_sym(name, sym);
368 free:
369 free_string(name);
370 return ret;
373 void register_param_set(int id)
375 my_id = id;
377 set_dynamic_states(my_id);
378 add_extra_mod_hook(&extra_mod_hook);
379 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
380 add_unmatched_state_hook(my_id, &unmatched_state);
381 add_merge_hook(my_id, &merge_estates);
382 add_split_return_callback(&print_return_value_param);