xref: /illumos-gate/usr/src/tools/smatch/src/smatch_stored_conditions.c (revision efe51d0cc2398b9ac179568b63a44e4bf295b8e2)
1 /*
2  * Copyright (C) 2014 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  * Keep a record of all the things we have tested for so that we know when we
20  * test for it again.  For example, if we have code like this:
21  *
22  * 	if (foo & FLAG)
23  *		lock();
24  *
25  *	...
26  *
27  *	if (foo & FLAG)
28  *		unlock();
29  *
30  * That's the end goal at least.  But actually implementing the flow of that
31  * requires quite a bit of work because if "foo" changes the condition needs to
32  * be retested and smatch_implications.c needs to be updated.
33  *
34  * For now, I just record the conditions and use it to see if we test for NULL
35  * twice.
36  *
37  */
38 
39 #include "smatch.h"
40 #include "smatch_slist.h"
41 
42 static int my_id;
43 static int link_id;
44 
alloc_link_state(struct expression_list * expr_list)45 static struct smatch_state *alloc_link_state(struct expression_list *expr_list)
46 {
47 	struct expression *tmp;
48 	struct smatch_state *state;
49 	static char buf[256];
50 	char *name;
51 	int i;
52 
53 	state = __alloc_smatch_state(0);
54 
55 	i = 0;
56 	FOR_EACH_PTR(expr_list, tmp) {
57 		name = expr_to_str(tmp);
58 		if (!i++) {
59 			snprintf(buf, sizeof(buf), "%s", name);
60 		} else {
61 			append(buf, ", ", sizeof(buf));
62 			append(buf, name, sizeof(buf));
63 		}
64 		free_string(name);
65 	} END_FOR_EACH_PTR(tmp);
66 
67 	state->name = alloc_sname(buf);
68 	state->data = expr_list;
69 	return state;
70 }
71 
clone_expression_list(struct expression_list * list)72 static struct expression_list *clone_expression_list(struct expression_list *list)
73 {
74 	struct expression_list *ret = NULL;
75 	struct expression *expr;
76 
77 	FOR_EACH_PTR(list, expr) {
78 		add_ptr_list(&ret, expr);
79 	} END_FOR_EACH_PTR(expr);
80 
81 	return ret;
82 }
83 
insert_expression(struct expression_list ** list,struct expression * expr)84 static void insert_expression(struct expression_list **list, struct expression *expr)
85 {
86 	struct expression *tmp;
87 
88 	FOR_EACH_PTR(*list, tmp) {
89 		if (tmp == expr)
90 			return;
91 	} END_FOR_EACH_PTR(tmp);
92 
93 	add_ptr_list(list, expr);
94 }
95 
merge_links(struct smatch_state * s1,struct smatch_state * s2)96 static struct smatch_state *merge_links(struct smatch_state *s1, struct smatch_state *s2)
97 {
98 	struct expression_list *list, *expr_list;
99 	struct expression *expr;
100 
101 	expr_list = clone_expression_list(s1->data);
102 
103 	list = s2->data;
104 	FOR_EACH_PTR(list, expr) {
105 		insert_expression(&expr_list, expr);
106 	} END_FOR_EACH_PTR(expr);
107 
108 	return alloc_link_state(expr_list);
109 }
110 
save_link_var_sym(const char * var,struct symbol * sym,struct expression * condition)111 static void save_link_var_sym(const char *var, struct symbol *sym, struct expression *condition)
112 {
113 	struct smatch_state *old_state, *new_state;
114 	struct expression_list *expr_list;
115 
116 	old_state = get_state(link_id, var, sym);
117 	expr_list = clone_expression_list(old_state ? old_state->data : NULL);
118 
119 	insert_expression(&expr_list, condition);
120 
121 	new_state = alloc_link_state(expr_list);
122 	set_state(link_id, var, sym, new_state);
123 }
124 
match_link_modify(struct sm_state * sm,struct expression * mod_expr)125 static void match_link_modify(struct sm_state *sm, struct expression *mod_expr)
126 {
127 	struct expression_list *expr_list;
128 	struct expression *tmp;
129 	char *name;
130 
131 	expr_list = sm->state->data;
132 
133 	FOR_EACH_PTR(expr_list, tmp) {
134 		name = expr_to_str(tmp);
135 		set_state(my_id, name, NULL, &undefined);
136 		free_string(name);
137 	} END_FOR_EACH_PTR(tmp);
138 	set_state(link_id, sm->name, sm->sym, &undefined);
139 }
140 
alloc_state(struct expression * expr,int is_true)141 static struct smatch_state *alloc_state(struct expression *expr, int is_true)
142 {
143 	struct smatch_state *state;
144 
145 	state = __alloc_smatch_state(0);
146 	if (is_true)
147 		state->name = alloc_sname("true");
148 	else
149 		state->name = alloc_sname("false");
150 	state->data = expr;
151 	return state;
152 }
153 
store_all_links(struct expression * expr,struct expression * condition)154 static void store_all_links(struct expression *expr, struct expression *condition)
155 {
156 	char *var;
157 	struct symbol *sym;
158 
159 	expr = strip_expr(expr);
160 
161 	if (is_array(expr)) {
162 		var = expr_to_known_chunk_sym(expr, &sym);
163 		if (var)
164 			save_link_var_sym(var, sym, condition);
165 	}
166 
167 	switch (expr->type) {
168 	case EXPR_COMPARE:
169 	case EXPR_BINOP:
170 		store_all_links(expr->left, condition);
171 		store_all_links(expr->right, condition);
172 		return;
173 	case EXPR_VALUE:
174 		return;
175 	}
176 
177 	var = expr_to_var_sym(expr, &sym);
178 	if (!var || !sym)
179 		goto free;
180 	save_link_var_sym(var, sym, condition);
181 free:
182 	free_string(var);
183 }
184 
condition_too_complicated(struct expression * expr)185 static int condition_too_complicated(struct expression *expr)
186 {
187 	if (get_complication_score(expr) > 2)
188 		return 1;
189 	return 0;
190 }
191 
__stored_condition(struct expression * expr)192 void __stored_condition(struct expression *expr)
193 {
194 	struct smatch_state *true_state, *false_state;
195 	char *name;
196 	sval_t val;
197 
198 	if (get_value(expr, &val))
199 		return;
200 
201 	if (condition_too_complicated(expr))
202 		return;
203 
204 	name = expr_to_str(expr);
205 	if (!name)
206 		return;
207 	true_state = alloc_state(expr, TRUE);
208 	false_state = alloc_state(expr, FALSE);
209 	set_true_false_states(my_id, name, NULL, true_state, false_state);
210 	store_all_links(expr, expr);
211 	free_string(name);
212 }
213 
get_stored_condition(struct expression * expr)214 struct smatch_state *get_stored_condition(struct expression *expr)
215 {
216 	struct smatch_state *state;
217 	char *name;
218 
219 	name = expr_to_str(expr);
220 	if (!name)
221 		return NULL;
222 
223 	state = get_state(my_id, name, NULL);
224 	free_string(name);
225 	return state;
226 }
227 
get_conditions(struct expression * expr)228 struct expression_list *get_conditions(struct expression *expr)
229 {
230 	struct smatch_state *state;
231 
232 	state = get_state_expr(link_id, expr);
233 	if (!state)
234 		return NULL;
235 	return state->data;
236 }
237 
register_stored_conditions(int id)238 void register_stored_conditions(int id)
239 {
240 	my_id = id;
241 	set_dynamic_states(my_id);
242 }
243 
register_stored_conditions_links(int id)244 void register_stored_conditions_links(int id)
245 {
246 	link_id = id;
247 	db_ignore_states(link_id);
248 	set_dynamic_states(link_id);
249 	add_merge_hook(link_id, &merge_links);
250 	add_modification_hook(link_id, &match_link_modify);
251 }
252 
253 #define RECURSE_LIMIT 50
254 
filter_by_sm(struct sm_state * sm,struct state_list ** true_stack,struct state_list ** false_stack,int * recurse_cnt)255 static void filter_by_sm(struct sm_state *sm,
256 		       struct state_list **true_stack,
257 		       struct state_list **false_stack,
258 		       int *recurse_cnt)
259 {
260 	if (!sm)
261 		return;
262 
263 	if ((*recurse_cnt)++ > RECURSE_LIMIT)
264 		return;
265 
266 	if (strcmp(sm->state->name, "true") == 0) {
267 		add_ptr_list(true_stack, sm);
268 	} else if (strcmp(sm->state->name, "false") == 0) {
269 		add_ptr_list(false_stack, sm);
270 	}
271 
272 	if (sm->merged) {
273 		filter_by_sm(sm->left, true_stack, false_stack, recurse_cnt);
274 		filter_by_sm(sm->right, true_stack, false_stack, recurse_cnt);
275 	}
276 }
277 
stored_condition_implication_hook(struct expression * expr,struct state_list ** true_stack,struct state_list ** false_stack)278 struct sm_state *stored_condition_implication_hook(struct expression *expr,
279 				struct state_list **true_stack,
280 				struct state_list **false_stack)
281 {
282 	struct sm_state *sm;
283 	char *name;
284 	int recurse_cnt = 0;
285 	struct state_list *tmp_true = NULL;
286 	struct state_list *tmp_false = NULL;
287 	struct sm_state *tmp;
288 
289 	expr = strip_expr(expr);
290 
291 	name = expr_to_str(expr);
292 	if (!name)
293 		return NULL;
294 
295 	sm = get_sm_state(my_id, name, NULL);
296 	free_string(name);
297 	if (!sm)
298 		return NULL;
299 	if (!sm->merged)
300 		return NULL;
301 
302 	filter_by_sm(sm, &tmp_true, &tmp_false, &recurse_cnt);
303 	if (!tmp_true && !tmp_false)
304 		return NULL;
305 	if (recurse_cnt > RECURSE_LIMIT) {
306 		sm = NULL;
307 		goto free;
308 	}
309 
310 	FOR_EACH_PTR(tmp_true, tmp) {
311 		add_ptr_list(true_stack, tmp);
312 	} END_FOR_EACH_PTR(tmp);
313 
314 	FOR_EACH_PTR(tmp_false, tmp) {
315 		add_ptr_list(false_stack, tmp);
316 	} END_FOR_EACH_PTR(tmp);
317 
318 free:
319 	free_slist(&tmp_true);
320 	free_slist(&tmp_false);
321 	return sm;
322 }
323 
324