2 * sparse/smatch_implied.c
4 * Copyright (C) 2008 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
11 * Imagine we have this code:
18 * if (foo == 99) // <-- point #2
19 * bar->baz; // <-- point #3
22 * At point #3 bar is non null and can be dereferenced.
24 * It's smatch_implied.c which sets bar to non null at point #2.
26 * At point #1 merge_slist() stores the list of states from both
27 * the true and false paths. On the true path foo == 99 and on
28 * the false path foo == 1. merge_slist() sets their my_pool
29 * list to show the other states which were there when foo == 99.
31 * When it comes to the if (foo == 99) the smatch implied hook
32 * looks for all the pools where foo was not 99. It makes a list
35 * Then for bar (and all the other states) it says, ok bar is a
36 * merged state that came from these previous states. We'll
37 * chop out all the states where it came from a pool where
38 * foo != 99 and merge it all back together.
40 * That is the implied state of bar.
42 * merge_slist() sets up ->my_pool. An sm_state only has one ->my_pool and
43 * that is the pool where it was first set. The my pool gets set when
44 * code paths merge. States that have been set since the last merge do
45 * not have a ->my_pool.
46 * merge_sm_state() sets ->left and ->right. (These are the states which were
47 * merged to form the current state.)
48 * a pool: a pool is an slist that has been merged with another slist.
52 #include "smatch_slist.h"
53 #include "smatch_extra.h"
55 static int print_count
= 0;
56 #define print_once(msg...) do { if (!print_count++) sm_msg(msg); } while (0)
57 #define DIMPLIED(msg...) do { if (option_debug_implied) printf(msg); } while (0)
59 int option_debug_implied
= 0;
60 int option_no_implied
= 0;
65 * It messes things up to free range list allocations. This helper fuction
66 * lets us reuse memory instead of doing new allocations.
68 static struct range_list
*tmp_range_list(long num
)
70 static struct range_list
*my_list
= NULL
;
71 static struct data_range
*my_range
;
73 __free_ptr_list((struct ptr_list
**)&my_list
);
74 my_range
= alloc_range(num
, num
);
77 add_ptr_list(&my_list
, my_range
);
81 static int pool_in_pools(struct state_list
*pool
,
82 struct state_list_stack
*pools
)
84 struct state_list
*tmp
;
86 FOR_EACH_PTR(pools
, tmp
) {
91 } END_FOR_EACH_PTR(tmp
);
95 static int is_checked(struct state_list
*checked
, struct sm_state
*sm
)
99 FOR_EACH_PTR(checked
, tmp
) {
102 } END_FOR_EACH_PTR(tmp
);
108 * Example code: if (foo == 99) {
110 * Say 'foo' is a merged state that has many possible values. It is the combination
111 * of merges. separate_pools() iterates through the pools recursively and makes a
112 * list of pools where foo == 99 and where foo != 99. If we don't know for sure which
113 * list a pool should be added to, then we don't add it to either.
116 static void separate_pools(struct sm_state
*sm_state
, int comparison
, struct range_list
*vals
,
118 struct state_list_stack
**true_stack
,
119 struct state_list_stack
**false_stack
,
120 struct state_list
**checked
)
124 int free_checked
= 0;
125 struct state_list
*checked_states
= NULL
;
131 Sometimes the implications are just too big to deal with
132 so we bail. Theoretically, bailing out here can cause more false
133 positives but won't hide actual bugs.
135 if (sm_state
->nr_children
> 5000) {
136 print_once("debug: seperate_pools %s nr_children %d", sm_state
->name
,
137 sm_state
->nr_children
);
141 if (checked
== NULL
) {
142 checked
= &checked_states
;
145 if (is_checked(*checked
, sm_state
))
147 add_ptr_list(checked
, sm_state
);
149 if (sm_state
->my_pool
) {
150 if (is_implied(sm_state
)) {
151 s
= get_sm_state_slist(sm_state
->my_pool
,
152 sm_state
->owner
, sm_state
->name
,
158 istrue
= !possibly_false_range_list(comparison
, get_dinfo(s
->state
), vals
, left
);
159 isfalse
= !possibly_true_range_list(comparison
, get_dinfo(s
->state
), vals
, left
);
161 if (option_debug_implied
|| option_debug
) {
162 if (istrue
&& isfalse
) {
163 printf("'%s = %s' from %d does not exist.\n",
164 s
->name
, show_state(s
->state
),
167 printf("'%s = %s' from %d is true.\n",
168 s
->name
, show_state(s
->state
),
170 } else if (isfalse
) {
171 printf("'%s = %s' from %d is false.\n",
172 s
->name
, show_state(s
->state
),
175 printf("'%s = %s' from %d could be true or "
177 show_state(s
->state
), s
->line
);
181 add_pool(true_stack
, s
->my_pool
);
184 add_pool(false_stack
, s
->my_pool
);
186 separate_pools(sm_state
->left
, comparison
, vals
, left
, true_stack
, false_stack
, checked
);
187 separate_pools(sm_state
->right
, comparison
, vals
, left
, true_stack
, false_stack
, checked
);
192 struct sm_state
*remove_my_pools(struct sm_state
*sm
,
193 struct state_list_stack
*pools
, int *modified
)
195 struct sm_state
*ret
= NULL
;
196 struct sm_state
*left
;
197 struct sm_state
*right
;
203 if (sm
->nr_children
> 5000) {
204 print_once("debug: remove_my_pools %s nr_children %d", sm
->name
,
209 if (pool_in_pools(sm
->my_pool
, pools
)) {
210 DIMPLIED("removed %s = %s from %d\n", sm
->name
,
211 show_state(sm
->state
), sm
->line
);
216 if (!is_merged(sm
)) {
217 DIMPLIED("kept %s = %s from %d\n", sm
->name
, show_state(sm
->state
),
222 DIMPLIED("checking %s = %s from %d\n", sm
->name
, show_state(sm
->state
), sm
->line
);
223 left
= remove_my_pools(sm
->left
, pools
, &removed
);
224 right
= remove_my_pools(sm
->right
, pools
, &removed
);
226 DIMPLIED("kept %s = %s from %d\n", sm
->name
, show_state(sm
->state
), sm
->line
);
230 if (!left
&& !right
) {
231 DIMPLIED("removed %s = %s from %d\n", sm
->name
, show_state(sm
->state
), sm
->line
);
236 ret
= clone_state(right
);
240 ret
->my_pool
= sm
->my_pool
;
242 ret
= clone_state(left
);
246 ret
->my_pool
= sm
->my_pool
;
248 ret
= merge_sm_states(left
, right
);
249 ret
->my_pool
= sm
->my_pool
;
252 DIMPLIED("partial %s = %s from %d\n", sm
->name
, show_state(sm
->state
), sm
->line
);
256 static struct state_list
*filter_stack(struct state_list
*pre_list
,
257 struct state_list_stack
*stack
)
259 struct state_list
*ret
= NULL
;
260 struct sm_state
*tmp
;
261 struct sm_state
*filtered_state
;
268 FOR_EACH_PTR(pre_list
, tmp
) {
270 filtered_state
= remove_my_pools(tmp
, stack
, &modified
);
271 if (filtered_state
&& modified
) {
272 add_ptr_list(&ret
, filtered_state
);
273 if ((counter
++)%10 && out_of_memory())
277 } END_FOR_EACH_PTR(tmp
);
281 static void get_eq_neq(struct sm_state
*sm_state
, int comparison
, struct range_list
*vals
,
283 struct state_list
*pre_list
,
284 struct state_list
**true_states
,
285 struct state_list
**false_states
)
287 struct state_list_stack
*true_stack
= NULL
;
288 struct state_list_stack
*false_stack
= NULL
;
290 if (option_debug_implied
|| option_debug
) {
292 sm_msg("checking implications: (%s %s %s)",
293 sm_state
->name
, show_special(comparison
), show_ranges(vals
));
295 sm_msg("checking implications: (%s %s %s)",
296 show_ranges(vals
), show_special(comparison
), sm_state
->name
);
299 separate_pools(sm_state
, comparison
, vals
, left
, &true_stack
, &false_stack
, NULL
);
301 DIMPLIED("filtering true stack.\n");
302 *true_states
= filter_stack(pre_list
, false_stack
);
303 DIMPLIED("filtering false stack.\n");
304 *false_states
= filter_stack(pre_list
, true_stack
);
305 free_stack(&true_stack
);
306 free_stack(&false_stack
);
307 if (option_debug_implied
|| option_debug
) {
308 printf("These are the implied states for the true path:\n");
309 __print_slist(*true_states
);
310 printf("These are the implied states for the false path:\n");
311 __print_slist(*false_states
);
315 static char *get_implication_variable(struct expression
*expr
, struct symbol
**symp
)
317 expr
= strip_expr(expr
);
318 if (expr
->type
== EXPR_ASSIGNMENT
)
319 return get_implication_variable(expr
->left
, symp
);
320 return get_variable_from_expr(expr
, symp
);
323 static void handle_comparison(struct expression
*expr
,
324 struct state_list
**implied_true
,
325 struct state_list
**implied_false
)
329 struct sm_state
*state
;
333 if (!get_value(expr
->left
, &value
)) {
334 if (!get_value(expr
->right
, &value
))
339 name
= get_implication_variable(expr
->left
, &sym
);
341 name
= get_implication_variable(expr
->right
, &sym
);
344 state
= get_sm_state(SMATCH_EXTRA
, name
, sym
);
347 if (!is_merged(state
)) {
348 DIMPLIED("%d '%s' is not merged.\n", get_lineno(), state
->name
);
351 get_eq_neq(state
, expr
->op
, tmp_range_list(value
), left
, __get_cur_slist(), implied_true
, implied_false
);
352 delete_state_slist(implied_true
, SMATCH_EXTRA
, name
, sym
);
353 delete_state_slist(implied_false
, SMATCH_EXTRA
, name
, sym
);
358 static void get_tf_states(struct expression
*expr
,
359 struct state_list
**implied_true
,
360 struct state_list
**implied_false
)
364 struct sm_state
*state
;
366 if (expr
->type
== EXPR_COMPARE
) {
367 handle_comparison(expr
, implied_true
, implied_false
);
370 if (expr
->type
== EXPR_ASSIGNMENT
) {
371 /* most of the time ->my_pools will be empty here because we
372 just set the state, but if have assigned a conditional
373 function there are implications. */
377 name
= get_variable_from_expr(expr
, &sym
);
380 state
= get_sm_state(SMATCH_EXTRA
, name
, sym
);
383 if (!is_merged(state
)) {
384 DIMPLIED("%d '%s' has no pools.\n", get_lineno(), state
->name
);
387 get_eq_neq(state
, SPECIAL_NOTEQUAL
, tmp_range_list(0), 1, __get_cur_slist(), implied_true
, implied_false
);
388 delete_state_slist(implied_true
, SMATCH_EXTRA
, name
, sym
);
389 delete_state_slist(implied_false
, SMATCH_EXTRA
, name
, sym
);
394 static void implied_states_hook(struct expression
*expr
)
396 struct sm_state
*state
;
397 struct state_list
*implied_true
= NULL
;
398 struct state_list
*implied_false
= NULL
;
400 if (option_no_implied
)
403 get_tf_states(expr
, &implied_true
, &implied_false
);
405 FOR_EACH_PTR(implied_true
, state
) {
406 __set_true_false_sm(state
, NULL
);
407 } END_FOR_EACH_PTR(state
);
408 free_slist(&implied_true
);
410 FOR_EACH_PTR(implied_false
, state
) {
411 __set_true_false_sm(NULL
, state
);
412 } END_FOR_EACH_PTR(state
);
413 free_slist(&implied_false
);
416 struct range_list
*__get_implied_values(struct expression
*switch_expr
)
420 struct smatch_state
*state
;
421 struct range_list
*ret
= NULL
;
423 name
= get_variable_from_expr(switch_expr
, &sym
);
426 state
= get_state(SMATCH_EXTRA
, name
, sym
);
429 ret
= clone_range_list(get_dinfo(state
)->value_ranges
);
433 add_range(&ret
, whole_range
.min
, whole_range
.max
);
437 void get_implications(char *name
, struct symbol
*sym
, int comparison
, int num
,
438 struct state_list
**true_states
,
439 struct state_list
**false_states
)
443 sm
= get_sm_state(SMATCH_EXTRA
, name
, sym
);
446 if (slist_has_state(sm
->possible
, &undefined
))
448 get_eq_neq(sm
, comparison
, tmp_range_list(num
), 1, __get_cur_slist(), true_states
, false_states
);
451 struct state_list
*__implied_case_slist(struct expression
*switch_expr
,
452 struct expression
*case_expr
,
453 struct range_list_stack
**remaining_cases
,
454 struct state_list
**raw_slist
)
459 struct sm_state
*true_sm
;
460 struct state_list
*true_states
= NULL
;
461 struct state_list
*false_states
= NULL
;
462 struct state_list
*ret
= clone_slist(*raw_slist
);
464 struct data_range
*range
;
465 struct range_list
*vals
= NULL
;
467 name
= get_variable_from_expr(switch_expr
, &sym
);
470 sm
= get_sm_state_slist(*raw_slist
, SMATCH_EXTRA
, name
, sym
);
472 vals
= top_range_list(*remaining_cases
);
474 if (!get_value(case_expr
, &val
)) {
477 filter_top_range_list(remaining_cases
, val
);
478 range
= alloc_range(val
, val
);
479 add_ptr_list(&vals
, range
);
483 get_eq_neq(sm
, SPECIAL_EQUAL
, vals
, 1, *raw_slist
, &true_states
, &false_states
);
485 true_sm
= get_sm_state_slist(true_states
, SMATCH_EXTRA
, name
, sym
);
487 set_state_slist(&true_states
, SMATCH_EXTRA
, name
, sym
, alloc_extra_state_range_list(vals
));
488 overwrite_slist(true_states
, &ret
);
489 free_slist(&true_states
);
490 free_slist(&false_states
);
496 static void match_end_func(struct symbol
*sym
)
501 void __extra_match_condition(struct expression
*expr
);
502 void register_implications(int id
)
504 add_hook(&implied_states_hook
, CONDITION_HOOK
);
505 add_hook(&__extra_match_condition
, CONDITION_HOOK
);
506 add_hook(&match_end_func
, END_FUNC_HOOK
);