db/kernel.return_fixes: add debugfs_initialized
[smatch.git] / smatch_param_set.c
blob609e1b7be01d12500f08c42721f7c49227e04dbb
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 "smatch.h"
36 #include "smatch_slist.h"
37 #include "smatch_extra.h"
39 static int my_id;
41 static struct smatch_state *unmatched_state(struct sm_state *sm)
43 return alloc_estate_empty();
46 static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state)
48 struct expression *faked;
49 char *left_name;
50 int ret = 0;
51 int len;
53 if (!__in_fake_assign)
54 return 0;
55 if (!is_whole_rl(estate_rl(state)))
56 return 0;
57 if (get_state(my_id, name, sym))
58 return 0;
60 faked = get_faked_expression();
61 if (!faked)
62 return 0;
63 if ((faked->type == EXPR_PREOP || faked->type == EXPR_POSTOP) &&
64 (faked->op == SPECIAL_INCREMENT || faked->op == SPECIAL_DECREMENT)) {
65 faked = strip_expr(faked->unop);
66 if (faked->type == EXPR_SYMBOL)
67 return 1;
68 return 0;
70 if (faked->type != EXPR_ASSIGNMENT)
71 return 0;
73 left_name = expr_to_var(faked->left);
74 if (!left_name)
75 return 0;
77 len = strlen(left_name);
78 if (strncmp(name, left_name, len) == 0 && name[len] == '-')
79 ret = 1;
80 free_string(left_name);
82 return ret;
85 static bool is_probably_worthless(struct expression *expr)
87 struct expression *faked;
89 if (!__in_fake_struct_assign)
90 return false;
92 faked = get_faked_expression();
93 if (!faked || faked->type != EXPR_ASSIGNMENT)
94 return false;
96 if (faked->left->type == EXPR_PREOP &&
97 faked->left->op == '*')
98 return false;
100 return true;
103 static bool name_is_sym_name(const char *name, struct symbol *sym)
105 if (!name || !sym || !sym->ident)
106 return false;
108 return strcmp(name, sym->ident->name) == 0;
111 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
113 struct symbol *param_sym;
114 struct symbol *type;
115 char *param_name;
117 if (expr && expr->smatch_flags & Fake)
118 return;
120 if (is_probably_worthless(expr))
121 return;
123 type = get_type(expr);
124 if (type && (type->type == SYM_STRUCT || type->type == SYM_UNION))
125 return;
127 if (name_is_sym_name(name, sym))
128 return;
130 param_name = get_param_var_sym_var_sym(name, sym, NULL, &param_sym);
131 if (!param_name || !param_sym)
132 goto free;
133 if (get_param_num_from_sym(param_sym) < 0)
134 goto free;
135 if (parent_is_set(param_name, param_sym, state))
136 return;
138 set_state(my_id, param_name, param_sym, state);
139 free:
140 free_string(param_name);
144 * This function is is a dirty hack because extra_mod_hook is giving us a NULL
145 * sym instead of a vsl.
147 static void match_array_assignment(struct expression *expr)
149 struct expression *array, *offset;
150 char *name;
151 struct symbol *sym;
152 struct range_list *rl;
153 sval_t sval;
154 char buf[256];
156 if (__in_fake_assign)
157 return;
159 if (!is_array(expr->left))
160 return;
161 array = get_array_base(expr->left);
162 offset = get_array_offset(expr->left);
164 /* These are handled by extra_mod_hook() */
165 if (get_value(offset, &sval))
166 return;
167 name = expr_to_var_sym(array, &sym);
168 if (!name || !sym)
169 goto free;
170 if (map_to_param(name, sym) < 0)
171 goto free;
172 get_absolute_rl(expr->right, &rl);
173 rl = cast_rl(get_type(expr->left), rl);
175 snprintf(buf, sizeof(buf), "*%s", name);
176 set_state(my_id, buf, sym, alloc_estate_rl(rl));
177 free:
178 free_string(name);
181 static char *get_two_dots(const char *name)
183 static char buf[80];
184 int i, cnt = 0;
186 for (i = 0; i < sizeof(buf); i++) {
187 if (name[i] == '.') {
188 cnt++;
189 if (cnt >= 2) {
190 buf[i] = '\0';
191 return buf;
194 buf[i] = name[i];
196 return NULL;
200 * This relies on the fact that these states are stored so that
201 * foo->bar is before foo->bar->baz.
203 static int parent_set(struct string_list *list, const char *param_name, struct sm_state *sm)
205 char *tmp;
206 int len;
207 int ret;
209 if (strncmp(param_name, "(*$)->", 6) == 0 && sm->sym && sm->sym->ident) {
210 char buf[64];
212 snprintf(buf, sizeof(buf), "*%s", sm->sym->ident->name);
213 if (get_state(my_id, buf, sm->sym))
214 return true;
217 FOR_EACH_PTR(list, tmp) {
218 len = strlen(tmp);
219 ret = strncmp(tmp, sm->name, len);
220 if (ret < 0)
221 continue;
222 if (ret > 0)
223 return 0;
224 if (sm->name[len] == '-')
225 return 1;
226 } END_FOR_EACH_PTR(tmp);
228 return 0;
231 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit)
233 struct sm_state *sm;
234 struct smatch_state *extra;
235 int param;
236 struct range_list *rl;
237 const char *param_name;
238 struct string_list *set_list = NULL;
239 char *math_str;
240 char buf[256];
241 char two_dot[80] = "";
242 int count = 0;
244 __promote_sets_to_clears(return_id, return_ranges, expr);
246 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
247 bool untracked = false;
249 if (!estate_rl(sm->state))
250 continue;
251 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
252 if (extra) {
253 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
254 if (!rl)
255 untracked = true;
256 } else {
257 rl = estate_rl(sm->state);
260 param = get_param_key_from_sm(sm, NULL, &param_name);
261 if (param < 0 || !param_name)
262 continue;
263 if (param_name[0] == '&')
264 continue;
265 if (strcmp(param_name, "$") == 0 ||
266 is_recursive_member(param_name) ||
267 is_ignored_kernel_data(param_name)) {
268 insert_string(&set_list, (char *)sm->name);
269 continue;
271 if (untracked) {
272 if (parent_was_PARAM_CLEAR(sm->name, sm->sym))
273 continue;
275 sql_insert_return_states(return_id, return_ranges,
276 UNTRACKED_PARAM, param, param_name, "");
277 continue;
280 if (limit) {
281 char *new = get_two_dots(param_name);
283 /* no useful information here. */
284 if (is_whole_rl(rl) && parent_set(set_list, param_name, sm))
285 continue;
287 if (new) {
288 if (strcmp(new, two_dot) == 0)
289 continue;
291 strncpy(two_dot, new, sizeof(two_dot));
292 insert_string(&set_list, (char *)sm->name);
293 sql_insert_return_states(return_id, return_ranges,
294 PARAM_SET, param, new, "s64min-s64max");
295 continue;
299 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
300 if (math_str) {
301 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
302 insert_string(&set_list, (char *)sm->name);
303 sql_insert_return_states(return_id, return_ranges,
304 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
305 param, param_name, buf);
306 continue;
309 /* no useful information here. */
310 if (is_whole_rl(rl) && parent_set(set_list, param_name, sm))
311 continue;
312 if (is_whole_rl(rl) && parent_was_PARAM_CLEAR(sm->name, sm->sym))
313 continue;
314 if (rl_is_zero(rl) && parent_was_PARAM_CLEAR_ZERO(sm->name, sm->sym))
315 continue;
317 insert_string(&set_list, (char *)sm->name);
319 sql_insert_return_states(return_id, return_ranges,
320 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
321 param, param_name, show_rl(rl));
322 if (limit && ++count > limit)
323 break;
325 } END_FOR_EACH_SM(sm);
327 free_ptr_list((struct ptr_list **)&set_list);
330 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
332 print_return_value_param_helper(return_id, return_ranges, expr, 0);
335 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
337 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
340 static int possibly_empty(struct sm_state *sm)
342 struct sm_state *tmp;
344 FOR_EACH_PTR(sm->possible, tmp) {
345 if (strcmp(tmp->name, "") == 0)
346 return 1;
347 } END_FOR_EACH_PTR(tmp);
348 return 0;
351 static bool sym_was_set(struct symbol *sym)
353 char buf[80];
355 if (!sym || !sym->ident)
356 return false;
358 snprintf(buf, sizeof(buf), "%s orig", sym->ident->name);
359 if (get_comparison_strings(sym->ident->name, buf) == SPECIAL_EQUAL)
360 return false;
362 return true;
365 int param_was_set_var_sym(const char *name, struct symbol *sym)
367 struct symbol *param_sym;
368 const char *param_name;
369 struct sm_state *sm;
370 char buf[80];
371 int len, i;
373 param_name = get_param_var_sym_var_sym(name, sym, NULL, &param_sym);
374 if (param_name && param_sym) {
375 name = param_name;
376 sym = param_sym;
379 if (!name)
380 return 0;
382 if (name[0] == '&')
383 name++;
385 if (sym_was_set(sym))
386 return true;
388 len = strlen(name);
389 if (len >= sizeof(buf))
390 len = sizeof(buf) - 1;
392 for (i = 0; i <= len; i++) {
393 if (name[i] != '-' && name[i] != '\0')
394 continue;
396 memcpy(buf, name, i);
397 buf[i] = '\0';
399 sm = get_sm_state(my_id, buf, sym);
400 if (!sm)
401 continue;
402 if (possibly_empty(sm))
403 continue;
404 return 1;
407 if (name[0] == '*')
408 return param_was_set_var_sym(name + 1, sym);
410 return 0;
413 static struct expression *get_unfaked_expr(struct expression *expr)
415 struct expression *tmp;
417 if (!is_fake_var(expr))
418 return expr;
419 tmp = expr_get_fake_parent_expr(expr);
420 if (!tmp || tmp->type != EXPR_ASSIGNMENT)
421 return expr;
422 return tmp->right;
425 int param_was_set(struct expression *expr)
427 char *name;
428 struct symbol *sym;
429 int ret = 0;
431 expr = get_unfaked_expr(expr);
433 name = expr_to_var_sym(expr, &sym);
434 if (!name || !sym)
435 goto free;
437 ret = param_was_set_var_sym(name, sym);
438 free:
439 free_string(name);
440 return ret;
443 void register_param_set(int id)
445 my_id = id;
447 set_dynamic_states(my_id);
448 add_extra_mod_hook(&extra_mod_hook);
449 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
450 add_unmatched_state_hook(my_id, &unmatched_state);
451 add_merge_hook(my_id, &merge_estates);
452 add_split_return_callback(&print_return_value_param);