unwind: paths where the parent is gone are counted as param_released
[smatch.git] / smatch_param_set.c
blobefab01f58d388eeabb0bf4fed48a4cca198e3dfb
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 (strcmp(param_name, "$") == 0) {
242 insert_string(&set_list, (char *)sm->name);
243 continue;
245 if (is_recursive_member(param_name)) {
246 insert_string(&set_list, (char *)sm->name);
247 continue;
250 if (is_ignored_kernel_data(param_name)) {
251 insert_string(&set_list, (char *)sm->name);
252 continue;
254 if (limit) {
255 char *new = get_two_dots(param_name);
257 if (new) {
258 if (strcmp(new, two_dot) == 0)
259 continue;
260 strncpy(two_dot, new, sizeof(two_dot));
261 sql_insert_return_states(return_id, return_ranges,
262 PARAM_SET, param, new, "s64min-s64max");
263 continue;
267 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
268 if (math_str) {
269 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
270 insert_string(&set_list, (char *)sm->name);
271 sql_insert_return_states(return_id, return_ranges,
272 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
273 param, param_name, buf);
274 continue;
277 /* no useful information here. */
278 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
279 continue;
280 insert_string(&set_list, (char *)sm->name);
282 sql_insert_return_states(return_id, return_ranges,
283 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
284 param, param_name, show_rl(rl));
285 if (limit && ++count > limit)
286 break;
288 } END_FOR_EACH_SM(sm);
290 free_ptr_list((struct ptr_list **)&set_list);
293 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
295 print_return_value_param_helper(return_id, return_ranges, expr, 0);
298 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
300 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
303 static int possibly_empty(struct sm_state *sm)
305 struct sm_state *tmp;
307 FOR_EACH_PTR(sm->possible, tmp) {
308 if (strcmp(tmp->name, "") == 0)
309 return 1;
310 } END_FOR_EACH_PTR(tmp);
311 return 0;
314 int param_was_set_var_sym(const char *name, struct symbol *sym)
316 struct sm_state *sm;
317 char buf[80];
318 int len, i;
320 if (!name)
321 return 0;
323 len = strlen(name);
324 if (len >= sizeof(buf))
325 len = sizeof(buf) - 1;
327 for (i = 0; i <= len; i++) {
328 if (name[i] != '-' && name[i] != '\0')
329 continue;
331 memcpy(buf, name, i);
332 buf[i] = '\0';
334 sm = get_sm_state(my_id, buf, sym);
335 if (!sm)
336 continue;
337 if (possibly_empty(sm))
338 continue;
339 return 1;
342 if (name[0] == '*')
343 return param_was_set_var_sym(name + 1, sym);
345 return 0;
348 int param_was_set(struct expression *expr)
350 char *name;
351 struct symbol *sym;
352 int ret = 0;
354 name = expr_to_var_sym(expr, &sym);
355 if (!name || !sym)
356 goto free;
358 ret = param_was_set_var_sym(name, sym);
359 free:
360 free_string(name);
361 return ret;
364 void register_param_set(int id)
366 my_id = id;
368 set_dynamic_states(my_id);
369 add_extra_mod_hook(&extra_mod_hook);
370 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
371 add_unmatched_state_hook(my_id, &unmatched_state);
372 add_merge_hook(my_id, &merge_estates);
373 add_split_return_callback(&print_return_value_param);