smatch.h: introduce sm_pedantic()
[smatch.git] / smatch_param_set.c
blobb2f9d4535f033ad73a5e5bbe79ca96092037a4ca
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 *name)
206 char *tmp;
207 int len;
208 int ret;
210 FOR_EACH_PTR(list, tmp) {
211 len = strlen(tmp);
212 ret = strncmp(tmp, name, len);
213 if (ret < 0)
214 continue;
215 if (ret > 0)
216 return 0;
217 if (name[len] == '-')
218 return 1;
219 } END_FOR_EACH_PTR(tmp);
221 return 0;
224 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit)
226 struct sm_state *sm;
227 struct smatch_state *extra;
228 int param;
229 struct range_list *rl;
230 const char *param_name;
231 struct string_list *set_list = NULL;
232 char *math_str;
233 char buf[256];
234 char two_dot[80] = "";
235 int count = 0;
237 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
238 if (!estate_rl(sm->state))
239 continue;
240 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
241 if (extra) {
242 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
243 if (!rl)
244 continue;
245 } else {
246 rl = estate_rl(sm->state);
249 param = get_param_key_from_sm(sm, NULL, &param_name);
250 if (param < 0 || !param_name)
251 continue;
252 if (param_name[0] == '&')
253 continue;
254 if (strcmp(param_name, "$") == 0 ||
255 is_recursive_member(param_name) ||
256 is_ignored_kernel_data(param_name)) {
257 insert_string(&set_list, (char *)sm->name);
258 continue;
260 if (limit) {
261 char *new = get_two_dots(param_name);
263 /* no useful information here. */
264 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
265 continue;
267 if (new) {
268 if (strcmp(new, two_dot) == 0)
269 continue;
271 strncpy(two_dot, new, sizeof(two_dot));
272 insert_string(&set_list, (char *)sm->name);
273 sql_insert_return_states(return_id, return_ranges,
274 PARAM_SET, param, new, "s64min-s64max");
275 continue;
279 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
280 if (math_str) {
281 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
282 insert_string(&set_list, (char *)sm->name);
283 sql_insert_return_states(return_id, return_ranges,
284 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
285 param, param_name, buf);
286 continue;
289 /* no useful information here. */
290 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
291 continue;
292 if (is_whole_rl(rl) && parent_was_PARAM_CLEAR(sm->name, sm->sym))
293 continue;
294 if (rl_is_zero(rl) && parent_was_PARAM_CLEAR_ZERO(sm->name, sm->sym))
295 continue;
297 insert_string(&set_list, (char *)sm->name);
299 sql_insert_return_states(return_id, return_ranges,
300 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
301 param, param_name, show_rl(rl));
302 if (limit && ++count > limit)
303 break;
305 } END_FOR_EACH_SM(sm);
307 free_ptr_list((struct ptr_list **)&set_list);
310 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
312 print_return_value_param_helper(return_id, return_ranges, expr, 0);
315 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
317 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
320 static int possibly_empty(struct sm_state *sm)
322 struct sm_state *tmp;
324 FOR_EACH_PTR(sm->possible, tmp) {
325 if (strcmp(tmp->name, "") == 0)
326 return 1;
327 } END_FOR_EACH_PTR(tmp);
328 return 0;
331 static bool sym_was_set(struct symbol *sym)
333 char buf[80];
335 if (!sym || !sym->ident)
336 return false;
338 snprintf(buf, sizeof(buf), "%s orig", sym->ident->name);
339 if (get_comparison_strings(sym->ident->name, buf) == SPECIAL_EQUAL)
340 return false;
342 return true;
345 int param_was_set_var_sym(const char *name, struct symbol *sym)
347 struct sm_state *sm;
348 char buf[80];
349 int len, i;
351 if (!name)
352 return 0;
354 if (name[0] == '&')
355 name++;
357 if (sym_was_set(sym))
358 return true;
360 len = strlen(name);
361 if (len >= sizeof(buf))
362 len = sizeof(buf) - 1;
364 for (i = 0; i <= len; i++) {
365 if (name[i] != '-' && name[i] != '\0')
366 continue;
368 memcpy(buf, name, i);
369 buf[i] = '\0';
371 sm = get_sm_state(my_id, buf, sym);
372 if (!sm)
373 continue;
374 if (possibly_empty(sm))
375 continue;
376 return 1;
379 if (name[0] == '*')
380 return param_was_set_var_sym(name + 1, sym);
382 return 0;
385 int param_was_set(struct expression *expr)
387 char *name;
388 struct symbol *sym;
389 int ret = 0;
391 name = expr_to_var_sym(expr, &sym);
392 if (!name || !sym)
393 goto free;
395 ret = param_was_set_var_sym(name, sym);
396 free:
397 free_string(name);
398 return ret;
401 void register_param_set(int id)
403 my_id = id;
405 set_dynamic_states(my_id);
406 add_extra_mod_hook(&extra_mod_hook);
407 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
408 add_unmatched_state_hook(my_id, &unmatched_state);
409 add_merge_hook(my_id, &merge_estates);
410 add_split_return_callback(&print_return_value_param);