smatch_kernel_host_data: enable additional debug
[smatch.git] / smatch_param_set.c
blob7bb561519684356006dc284803d27fbb6095a72b
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 sql_insert_return_states(return_id, return_ranges,
274 UNTRACKED_PARAM, param, param_name, "");
275 continue;
278 if (limit) {
279 char *new = get_two_dots(param_name);
281 /* no useful information here. */
282 if (is_whole_rl(rl) && parent_set(set_list, param_name, sm))
283 continue;
285 if (new) {
286 if (strcmp(new, two_dot) == 0)
287 continue;
289 strncpy(two_dot, new, sizeof(two_dot));
290 insert_string(&set_list, (char *)sm->name);
291 sql_insert_return_states(return_id, return_ranges,
292 PARAM_SET, param, new, "s64min-s64max");
293 continue;
297 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
298 if (math_str) {
299 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
300 insert_string(&set_list, (char *)sm->name);
301 sql_insert_return_states(return_id, return_ranges,
302 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
303 param, param_name, buf);
304 continue;
307 /* no useful information here. */
308 if (is_whole_rl(rl) && parent_set(set_list, param_name, sm))
309 continue;
310 if (is_whole_rl(rl) && parent_was_PARAM_CLEAR(sm->name, sm->sym))
311 continue;
312 if (rl_is_zero(rl) && parent_was_PARAM_CLEAR_ZERO(sm->name, sm->sym))
313 continue;
315 insert_string(&set_list, (char *)sm->name);
317 sql_insert_return_states(return_id, return_ranges,
318 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
319 param, param_name, show_rl(rl));
320 if (limit && ++count > limit)
321 break;
323 } END_FOR_EACH_SM(sm);
325 free_ptr_list((struct ptr_list **)&set_list);
328 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
330 print_return_value_param_helper(return_id, return_ranges, expr, 0);
333 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
335 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
338 static int possibly_empty(struct sm_state *sm)
340 struct sm_state *tmp;
342 FOR_EACH_PTR(sm->possible, tmp) {
343 if (strcmp(tmp->name, "") == 0)
344 return 1;
345 } END_FOR_EACH_PTR(tmp);
346 return 0;
349 static bool sym_was_set(struct symbol *sym)
351 char buf[80];
353 if (!sym || !sym->ident)
354 return false;
356 snprintf(buf, sizeof(buf), "%s orig", sym->ident->name);
357 if (get_comparison_strings(sym->ident->name, buf) == SPECIAL_EQUAL)
358 return false;
360 return true;
363 int param_was_set_var_sym(const char *name, struct symbol *sym)
365 struct sm_state *sm;
366 char buf[80];
367 int len, i;
369 if (!name)
370 return 0;
372 if (name[0] == '&')
373 name++;
375 if (sym_was_set(sym))
376 return true;
378 len = strlen(name);
379 if (len >= sizeof(buf))
380 len = sizeof(buf) - 1;
382 for (i = 0; i <= len; i++) {
383 if (name[i] != '-' && name[i] != '\0')
384 continue;
386 memcpy(buf, name, i);
387 buf[i] = '\0';
389 sm = get_sm_state(my_id, buf, sym);
390 if (!sm)
391 continue;
392 if (possibly_empty(sm))
393 continue;
394 return 1;
397 if (name[0] == '*')
398 return param_was_set_var_sym(name + 1, sym);
400 return 0;
403 static struct expression *get_unfaked_expr(struct expression *expr)
405 struct expression *tmp;
407 if (!is_fake_var(expr))
408 return expr;
409 tmp = expr_get_fake_parent_expr(expr);
410 if (!tmp || tmp->type != EXPR_ASSIGNMENT)
411 return expr;
412 return tmp->right;
415 int param_was_set(struct expression *expr)
417 char *name;
418 struct symbol *sym;
419 int ret = 0;
421 expr = get_unfaked_expr(expr);
423 name = expr_to_var_sym(expr, &sym);
424 if (!name || !sym)
425 goto free;
427 ret = param_was_set_var_sym(name, sym);
428 free:
429 free_string(name);
430 return ret;
433 void register_param_set(int id)
435 my_id = id;
437 set_dynamic_states(my_id);
438 add_extra_mod_hook(&extra_mod_hook);
439 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
440 add_unmatched_state_hook(my_id, &unmatched_state);
441 add_merge_hook(my_id, &merge_estates);
442 add_split_return_callback(&print_return_value_param);