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