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 
42 static struct smatch_state *unmatched_state(struct sm_state *sm)
43 {
44 	return alloc_estate_empty();
45 }
46 
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 
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  */
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 
133 /*
134  * This relies on the fact that these states are stored so that
135  * foo->bar is before foo->bar->baz.
136  */
137 static int parent_set(struct string_list *list, const char *name)
138 {
139 	char *tmp;
140 	int len;
141 	int ret;
142 
143 	FOR_EACH_PTR(list, tmp) {
144 		len = strlen(tmp);
145 		ret = strncmp(tmp, name, len);
146 		if (ret < 0)
147 			continue;
148 		if (ret > 0)
149 			return 0;
150 		if (name[len] == '-')
151 			return 1;
152 	} END_FOR_EACH_PTR(tmp);
153 
154 	return 0;
155 }
156 
157 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
158 {
159 	struct sm_state *sm;
160 	struct smatch_state *extra;
161 	int param;
162 	struct range_list *rl;
163 	const char *param_name;
164 	struct string_list *set_list = NULL;
165 	char *math_str;
166 	char buf[256];
167 
168 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
169 		if (!estate_rl(sm->state))
170 			continue;
171 		extra = get_state(SMATCH_EXTRA, sm->name, sm->sym);
172 		if (extra) {
173 			rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
174 			if (!rl)
175 				continue;
176 		} else {
177 			rl = estate_rl(sm->state);
178 		}
179 
180 		param = get_param_num_from_sym(sm->sym);
181 		if (param < 0)
182 			continue;
183 		param_name = get_param_name(sm);
184 		if (!param_name)
185 			continue;
186 		if (strcmp(param_name, "$") == 0) {
187 			insert_string(&set_list, (char *)sm->name);
188 			continue;
189 		}
190 		if (is_recursive_member(param_name)) {
191 			insert_string(&set_list, (char *)sm->name);
192 			continue;
193 		}
194 
195 		if (is_ignored_kernel_data(param_name)) {
196 			insert_string(&set_list, (char *)sm->name);
197 			continue;
198 		}
199 
200 		math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
201 		if (math_str) {
202 			snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
203 			insert_string(&set_list, (char *)sm->name);
204 			sql_insert_return_states(return_id, return_ranges,
205 					param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
206 					param, param_name, buf);
207 			continue;
208 		}
209 
210 		/* no useful information here. */
211 		if (is_whole_rl(rl) && parent_set(set_list, sm->name))
212 			continue;
213 		insert_string(&set_list, (char *)sm->name);
214 
215 		sql_insert_return_states(return_id, return_ranges,
216 					 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
217 					 param, param_name, show_rl(rl));
218 
219 	} END_FOR_EACH_SM(sm);
220 
221 	free_ptr_list((struct ptr_list **)&set_list);
222 }
223 
224 int param_was_set_var_sym(const char *name, struct symbol *sym)
225 {
226 	struct sm_state *sm;
227 	int len;
228 
229 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
230 		if (sm->sym != sym)
231 			continue;
232 		len = strlen(sm->name);
233 		if (strncmp(sm->name, name, len) != 0)
234 			continue;
235 		if (name[len] == '\0' ||
236 		    name[len] == '-')
237 			return 1;
238 	} END_FOR_EACH_SM(sm);
239 
240 	return 0;
241 }
242 
243 int param_was_set(struct expression *expr)
244 {
245 	char *name;
246 	struct symbol *sym;
247 	int ret = 0;
248 
249 	name = expr_to_var_sym(expr, &sym);
250 	if (!name || !sym)
251 		goto free;
252 
253 	ret = param_was_set_var_sym(name, sym);
254 free:
255 	free_string(name);
256 	return ret;
257 }
258 
259 void register_param_set(int id)
260 {
261 	my_id = id;
262 
263 	set_dynamic_states(my_id);
264 	add_extra_mod_hook(&extra_mod_hook);
265 	add_hook(match_array_assignment, ASSIGNMENT_HOOK);
266 	add_unmatched_state_hook(my_id, &unmatched_state);
267 	add_merge_hook(my_id, &merge_estates);
268 	add_split_return_callback(&print_return_value_param);
269 }
270 
271