1 /*
2 * Copyright (C) 2012 Oracle.
3 *
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.
8 *
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.
13 *
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
16 */
17
18 /*
19 * This is for functions like:
20 *
21 * int foo(int *x)
22 * {
23 * if (*x == 42) {
24 * *x = 0;
25 * return 1;
26 * }
27 * return 0;
28 * }
29 *
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.
32 *
33 */
34
35 #include "scope.h"
36 #include "smatch.h"
37 #include "smatch_slist.h"
38 #include "smatch_extra.h"
39
40 static int my_id;
41
unmatched_state(struct sm_state * sm)42 static struct smatch_state *unmatched_state(struct sm_state *sm)
43 {
44 return alloc_estate_empty();
45 }
46
parent_is_set(const char * name,struct symbol * sym,struct smatch_state * state)47 static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state)
48 {
49 struct expression *faked;
50 char *left_name;
51 int ret = 0;
52 int len;
53
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;
60
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;
70 }
71 if (faked->type != EXPR_ASSIGNMENT)
72 return 0;
73
74 left_name = expr_to_var(faked->left);
75 if (!left_name)
76 return 0;
77
78 len = strlen(left_name);
79 if (strncmp(name, left_name, len) == 0 && name[len] == '-')
80 ret = 1;
81 free_string(left_name);
82
83 return ret;
84 }
85
extra_mod_hook(const char * name,struct symbol * sym,struct expression * expr,struct smatch_state * state)86 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
87 {
88 if (parent_is_set(name, sym, state))
89 return;
90 if (get_param_num_from_sym(sym) < 0)
91 return;
92 set_state(my_id, name, sym, state);
93 }
94
95 /*
96 * This function is is a dirty hack because extra_mod_hook is giving us a NULL
97 * sym instead of a vsl.
98 */
match_array_assignment(struct expression * expr)99 static void match_array_assignment(struct expression *expr)
100 {
101 struct expression *array, *offset;
102 char *name;
103 struct symbol *sym;
104 struct range_list *rl;
105 sval_t sval;
106 char buf[256];
107
108 if (__in_fake_assign)
109 return;
110
111 if (!is_array(expr->left))
112 return;
113 array = get_array_base(expr->left);
114 offset = get_array_offset(expr->left);
115
116 /* These are handled by extra_mod_hook() */
117 if (get_value(offset, &sval))
118 return;
119 name = expr_to_var_sym(array, &sym);
120 if (!name || !sym)
121 goto free;
122 if (get_param_num_from_sym(sym) < 0)
123 goto free;
124 get_absolute_rl(expr->right, &rl);
125 rl = cast_rl(get_type(expr->left), rl);
126
127 snprintf(buf, sizeof(buf), "*%s", name);
128 set_state(my_id, buf, sym, alloc_estate_rl(rl));
129 free:
130 free_string(name);
131 }
132
get_two_dots(const char * name)133 static char *get_two_dots(const char *name)
134 {
135 static char buf[80];
136 int i, cnt = 0;
137
138 for (i = 0; i < sizeof(buf); i++) {
139 if (name[i] == '.') {
140 cnt++;
141 if (cnt >= 2) {
142 buf[i] = '\0';
143 return buf;
144 }
145 }
146 buf[i] = name[i];
147 }
148 return NULL;
149 }
150
151 /*
152 * This relies on the fact that these states are stored so that
153 * foo->bar is before foo->bar->baz.
154 */
parent_set(struct string_list * list,const char * name)155 static int parent_set(struct string_list *list, const char *name)
156 {
157 char *tmp;
158 int len;
159 int ret;
160
161 FOR_EACH_PTR(list, tmp) {
162 len = strlen(tmp);
163 ret = strncmp(tmp, name, len);
164 if (ret < 0)
165 continue;
166 if (ret > 0)
167 return 0;
168 if (name[len] == '-')
169 return 1;
170 } END_FOR_EACH_PTR(tmp);
171
172 return 0;
173 }
174
print_return_value_param_helper(int return_id,char * return_ranges,struct expression * expr,int limit)175 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit)
176 {
177 struct sm_state *sm;
178 struct smatch_state *extra;
179 int param;
180 struct range_list *rl;
181 const char *param_name;
182 struct string_list *set_list = NULL;
183 char *math_str;
184 char buf[256];
185 char two_dot[80] = "";
186 int count = 0;
187
188 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
189 if (!estate_rl(sm->state))
190 continue;
191 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
192 if (extra) {
193 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
194 if (!rl)
195 continue;
196 } else {
197 rl = estate_rl(sm->state);
198 }
199
200 param = get_param_num_from_sym(sm->sym);
201 if (param < 0)
202 continue;
203 param_name = get_param_name(sm);
204 if (!param_name)
205 continue;
206 if (strcmp(param_name, "$") == 0) {
207 insert_string(&set_list, (char *)sm->name);
208 continue;
209 }
210 if (is_recursive_member(param_name)) {
211 insert_string(&set_list, (char *)sm->name);
212 continue;
213 }
214
215 if (is_ignored_kernel_data(param_name)) {
216 insert_string(&set_list, (char *)sm->name);
217 continue;
218 }
219 if (limit) {
220 char *new = get_two_dots(param_name);
221
222 if (new) {
223 if (strcmp(new, two_dot) == 0)
224 continue;
225 strncpy(two_dot, new, sizeof(two_dot));
226 sql_insert_return_states(return_id, return_ranges,
227 PARAM_SET, param, new, "s64min-s64max");
228 continue;
229 }
230 }
231
232 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
233 if (math_str) {
234 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
235 insert_string(&set_list, (char *)sm->name);
236 sql_insert_return_states(return_id, return_ranges,
237 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
238 param, param_name, buf);
239 continue;
240 }
241
242 /* no useful information here. */
243 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
244 continue;
245 insert_string(&set_list, (char *)sm->name);
246
247 sql_insert_return_states(return_id, return_ranges,
248 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
249 param, param_name, show_rl(rl));
250 if (limit && ++count > limit)
251 break;
252
253 } END_FOR_EACH_SM(sm);
254
255 free_ptr_list((struct ptr_list **)&set_list);
256 }
257
print_return_value_param(int return_id,char * return_ranges,struct expression * expr)258 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
259 {
260 print_return_value_param_helper(return_id, return_ranges, expr, 0);
261 }
262
print_limited_param_set(int return_id,char * return_ranges,struct expression * expr)263 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
264 {
265 print_return_value_param_helper(return_id, return_ranges, expr, 1000);
266 }
267
possibly_empty(struct sm_state * sm)268 static int possibly_empty(struct sm_state *sm)
269 {
270 struct sm_state *tmp;
271
272 FOR_EACH_PTR(sm->possible, tmp) {
273 if (strcmp(tmp->name, "") == 0)
274 return 1;
275 } END_FOR_EACH_PTR(tmp);
276 return 0;
277 }
278
param_was_set_var_sym(const char * name,struct symbol * sym)279 int param_was_set_var_sym(const char *name, struct symbol *sym)
280 {
281 struct sm_state *sm;
282 char buf[80];
283 int len, i;
284
285 if (!name)
286 return 0;
287
288 len = strlen(name);
289 if (len >= sizeof(buf))
290 len = sizeof(buf) - 1;
291
292 for (i = 0; i <= len; i++) {
293 if (name[i] != '-' && name[i] != '\0')
294 continue;
295
296 memcpy(buf, name, i);
297 buf[i] = '\0';
298
299 sm = get_sm_state(my_id, buf, sym);
300 if (!sm)
301 continue;
302 if (possibly_empty(sm))
303 continue;
304 return 1;
305 }
306
307 if (name[0] == '*')
308 return param_was_set_var_sym(name + 1, sym);
309
310 return 0;
311 }
312
param_was_set(struct expression * expr)313 int param_was_set(struct expression *expr)
314 {
315 char *name;
316 struct symbol *sym;
317 int ret = 0;
318
319 name = expr_to_var_sym(expr, &sym);
320 if (!name || !sym)
321 goto free;
322
323 ret = param_was_set_var_sym(name, sym);
324 free:
325 free_string(name);
326 return ret;
327 }
328
register_param_set(int id)329 void register_param_set(int id)
330 {
331 my_id = id;
332
333 set_dynamic_states(my_id);
334 add_extra_mod_hook(&extra_mod_hook);
335 add_hook(match_array_assignment, ASSIGNMENT_HOOK);
336 add_unmatched_state_hook(my_id, &unmatched_state);
337 add_merge_hook(my_id, &merge_estates);
338 add_split_return_callback(&print_return_value_param);
339 }
340
341