param_key: fix container of when no struct member is referenced
[smatch.git] / smatch_param_set.c
blob9fdc6c110d2193f1ce52fb8f450e4ec989d74de4
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 bool name_is_sym_name(const char *name, struct symbol *sym)
106 if (!name || !sym || !sym->ident)
107 return false;
109 return strcmp(name, sym->ident->name) == 0;
112 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
114 struct symbol *param_sym;
115 struct symbol *type;
116 char *param_name;
118 if (expr && expr->smatch_flags & Fake)
119 return;
121 if (is_probably_worthless(expr))
122 return;
124 type = get_type(expr);
125 if (type && (type->type == SYM_STRUCT || type->type == SYM_UNION))
126 return;
128 if (name_is_sym_name(name, sym))
129 return;
131 param_name = get_param_var_sym_var_sym(name, sym, NULL, &param_sym);
132 if (!param_name || !param_sym)
133 goto free;
134 if (get_param_num_from_sym(param_sym) < 0)
135 goto free;
136 if (parent_is_set(param_name, param_sym, state))
137 return;
139 set_state(my_id, param_name, param_sym, state);
140 free:
141 free_string(param_name);
145 * This function is is a dirty hack because extra_mod_hook is giving us a NULL
146 * sym instead of a vsl.
148 static void match_array_assignment(struct expression *expr)
150 struct expression *array, *offset;
151 char *name;
152 struct symbol *sym;
153 struct range_list *rl;
154 sval_t sval;
155 char buf[256];
157 if (__in_fake_assign)
158 return;
160 if (!is_array(expr->left))
161 return;
162 array = get_array_base(expr->left);
163 offset = get_array_offset(expr->left);
165 /* These are handled by extra_mod_hook() */
166 if (get_value(offset, &sval))
167 return;
168 name = expr_to_var_sym(array, &sym);
169 if (!name || !sym)
170 goto free;
171 if (map_to_param(name, sym) < 0)
172 goto free;
173 get_absolute_rl(expr->right, &rl);
174 rl = cast_rl(get_type(expr->left), rl);
176 snprintf(buf, sizeof(buf), "*%s", name);
177 set_state(my_id, buf, sym, alloc_estate_rl(rl));
178 free:
179 free_string(name);
182 static char *get_two_dots(const char *name)
184 static char buf[80];
185 int i, cnt = 0;
187 for (i = 0; i < sizeof(buf); i++) {
188 if (name[i] == '.') {
189 cnt++;
190 if (cnt >= 2) {
191 buf[i] = '\0';
192 return buf;
195 buf[i] = name[i];
197 return NULL;
201 * This relies on the fact that these states are stored so that
202 * foo->bar is before foo->bar->baz.
204 static int parent_set(struct string_list *list, const char *param_name, struct sm_state *sm)
206 char *tmp;
207 int len;
208 int ret;
210 if (strncmp(param_name, "(*$)->", 6) == 0 && sm->sym && sm->sym->ident) {
211 char buf[64];
213 snprintf(buf, sizeof(buf), "*%s", sm->sym->ident->name);
214 if (get_state(my_id, buf, sm->sym))
215 return true;
218 FOR_EACH_PTR(list, tmp) {
219 len = strlen(tmp);
220 ret = strncmp(tmp, sm->name, len);
221 if (ret < 0)
222 continue;
223 if (ret > 0)
224 return 0;
225 if (sm->name[len] == '-')
226 return 1;
227 } END_FOR_EACH_PTR(tmp);
229 return 0;
232 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit)
234 struct sm_state *sm;
235 struct smatch_state *extra;
236 int param;
237 struct range_list *rl;
238 const char *param_name;
239 struct string_list *set_list = NULL;
240 char *math_str;
241 char buf[256];
242 char two_dot[80] = "";
243 int count = 0;
245 __promote_sets_to_clears(return_id, return_ranges, expr);
247 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
248 bool untracked = false;
250 if (!estate_rl(sm->state))
251 continue;
252 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
253 if (extra) {
254 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
255 if (!rl)
256 untracked = true;
257 } else {
258 rl = estate_rl(sm->state);
261 param = get_param_key_from_sm(sm, NULL, &param_name);
262 if (param < 0 || !param_name)
263 continue;
264 if (param_name[0] == '&')
265 continue;
266 if (strcmp(param_name, "$") == 0 ||
267 is_recursive_member(param_name) ||
268 is_ignored_kernel_data(param_name)) {
269 insert_string(&set_list, (char *)sm->name);
270 continue;
272 if (untracked) {
273 if (parent_was_PARAM_CLEAR(sm->name, sm->sym))
274 continue;
276 sql_insert_return_states(return_id, return_ranges,
277 UNTRACKED_PARAM, param, param_name, "");
278 continue;
281 if (limit) {
282 char *new = get_two_dots(param_name);
284 /* no useful information here. */
285 if (is_whole_rl(rl) && parent_set(set_list, param_name, sm))
286 continue;
288 if (new) {
289 if (strcmp(new, two_dot) == 0)
290 continue;
292 strncpy(two_dot, new, sizeof(two_dot));
293 insert_string(&set_list, (char *)sm->name);
294 sql_insert_return_states(return_id, return_ranges,
295 PARAM_SET, param, new, "s64min-s64max");
296 continue;
300 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
301 if (math_str) {
302 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
303 insert_string(&set_list, (char *)sm->name);
304 sql_insert_return_states(return_id, return_ranges,
305 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
306 param, param_name, buf);
307 continue;
310 /* no useful information here. */
311 if (is_whole_rl(rl) && parent_set(set_list, param_name, sm))
312 continue;
313 if (is_whole_rl(rl) && parent_was_PARAM_CLEAR(sm->name, sm->sym))
314 continue;
315 if (rl_is_zero(rl) && parent_was_PARAM_CLEAR_ZERO(sm->name, sm->sym))
316 continue;
318 insert_string(&set_list, (char *)sm->name);
320 sql_insert_return_states(return_id, return_ranges,
321 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
322 param, param_name, show_rl(rl));
323 if (limit && ++count > limit)
324 break;
326 } END_FOR_EACH_SM(sm);
328 free_ptr_list((struct ptr_list **)&set_list);
331 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
333 print_return_value_param_helper(return_id, return_ranges, expr, 0);
336 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
338 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
341 static int possibly_empty(struct sm_state *sm)
343 struct sm_state *tmp;
345 FOR_EACH_PTR(sm->possible, tmp) {
346 if (strcmp(tmp->name, "") == 0)
347 return 1;
348 } END_FOR_EACH_PTR(tmp);
349 return 0;
352 static bool sym_was_set(struct symbol *sym)
354 char buf[80];
356 if (!sym || !sym->ident)
357 return false;
359 snprintf(buf, sizeof(buf), "%s orig", sym->ident->name);
360 if (get_comparison_strings(sym->ident->name, buf) == SPECIAL_EQUAL)
361 return false;
363 return true;
366 int param_was_set_var_sym(const char *name, struct symbol *sym)
368 struct symbol *param_sym;
369 const char *param_name;
370 struct sm_state *sm;
371 char buf[80];
372 int len, i;
374 param_name = get_param_var_sym_var_sym(name, sym, NULL, &param_sym);
375 if (param_name && param_sym) {
376 name = param_name;
377 sym = param_sym;
380 if (!name)
381 return 0;
383 if (name[0] == '&')
384 name++;
386 if (sym_was_set(sym))
387 return true;
389 len = strlen(name);
390 if (len >= sizeof(buf))
391 len = sizeof(buf) - 1;
393 for (i = 0; i <= len; i++) {
394 if (name[i] != '-' && name[i] != '\0')
395 continue;
397 memcpy(buf, name, i);
398 buf[i] = '\0';
400 sm = get_sm_state(my_id, buf, sym);
401 if (!sm)
402 continue;
403 if (possibly_empty(sm))
404 continue;
405 return 1;
408 if (name[0] == '*')
409 return param_was_set_var_sym(name + 1, sym);
411 return 0;
414 static struct expression *get_unfaked_expr(struct expression *expr)
416 struct expression *tmp;
418 if (!is_fake_var(expr))
419 return expr;
420 tmp = expr_get_fake_parent_expr(expr);
421 if (!tmp || tmp->type != EXPR_ASSIGNMENT)
422 return expr;
423 return tmp->right;
426 int param_was_set(struct expression *expr)
428 char *name;
429 struct symbol *sym;
430 int ret = 0;
432 expr = get_unfaked_expr(expr);
434 name = expr_to_var_sym(expr, &sym);
435 if (!name || !sym)
436 goto free;
438 ret = param_was_set_var_sym(name, sym);
439 free:
440 free_string(name);
441 return ret;
444 void register_param_set(int id)
446 my_id = id;
448 set_dynamic_states(my_id);
449 add_extra_mod_hook(&extra_mod_hook);
450 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
451 add_unmatched_state_hook(my_id, &unmatched_state);
452 add_merge_hook(my_id, &merge_estates);
453 add_split_return_callback(&print_return_value_param);