2 * Copyright (C) 2016 Oracle.
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.
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.
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
21 #include "smatch_extra.h"
22 #include "smatch_slist.h"
30 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
33 * We default to decremented. For example, say we have:
36 * <- p is decreemented.
39 if ((sm
->state
== &dec
) &&
40 parent_is_gone_var_sym(sm
->name
, sm
->sym
))
45 static struct stree
*start_states
;
46 static struct stree_stack
*saved_stack
;
47 static void set_start_state(const char *name
, struct symbol
*sym
, struct smatch_state
*start
)
49 struct smatch_state
*orig
;
51 orig
= get_state_stree(start_states
, my_id
, name
, sym
);
53 set_state_stree(&start_states
, my_id
, name
, sym
, start
);
54 else if (orig
!= start
)
55 set_state_stree(&start_states
, my_id
, name
, sym
, &undefined
);
58 static struct sm_state
*get_best_match(const char *key
)
61 struct sm_state
*match
;
63 int start_pos
, state_len
, key_len
, chunks
, i
;
65 if (strncmp(key
, "$->", 3) == 0)
68 key_len
= strlen(key
);
70 for (i
= key_len
- 1; i
> 0; i
--) {
71 if (key
[i
] == '>' || key
[i
] == '.')
75 key_len
= strlen(key
);
80 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
81 state_len
= strlen(sm
->name
);
82 if (state_len
< key_len
)
84 start_pos
= state_len
- key_len
;
85 if ((start_pos
== 0 || !isalnum(sm
->name
[start_pos
- 1])) &&
86 strcmp(sm
->name
+ start_pos
, key
) == 0) {
90 } END_FOR_EACH_SM(sm
);
97 static void db_inc_dec(struct expression
*expr
, int param
, const char *key
, const char *value
, int inc_dec
)
99 struct sm_state
*start_sm
;
100 struct expression
*arg
;
103 bool free_at_end
= true;
105 while (expr
->type
== EXPR_ASSIGNMENT
)
106 expr
= strip_expr(expr
->right
);
107 if (expr
->type
!= EXPR_CALL
)
110 arg
= get_argument_from_call_expr(expr
->args
, param
);
114 name
= get_variable_from_key(arg
, key
, &sym
);
118 start_sm
= get_sm_state(my_id
, name
, sym
);
119 if (!start_sm
&& inc_dec
== ATOMIC_DEC
) {
120 start_sm
= get_best_match(key
);
124 name
= (char *)start_sm
->name
;
129 if (inc_dec
== ATOMIC_INC
) {
131 set_start_state(name
, sym
, &dec
);
132 // set_refcount_inc(name, sym);
133 set_state(my_id
, name
, sym
, &inc
);
135 // set_refcount_dec(name, sym);
137 set_start_state(name
, sym
, &inc
);
139 if (start_sm
&& start_sm
->state
== &inc
)
140 set_state(my_id
, name
, sym
, &start_state
);
142 set_state(my_id
, name
, sym
, &dec
);
150 static const char *primitive_funcs
[] = {
154 "atomic_sub_and_test",
155 "atomic_dec_and_test",
156 "_atomic_dec_and_lock",
165 "refcount_add_not_zero",
166 "refcount_inc_not_zero",
167 "refcount_sub_and_test",
168 "refcount_dec_and_test",
169 "atomic_dec_if_positive",
172 static bool is_inc_dec_primitive(struct expression
*expr
)
176 while (expr
->type
== EXPR_ASSIGNMENT
)
177 expr
= strip_expr(expr
->right
);
178 if (expr
->type
!= EXPR_CALL
)
181 if (expr
->fn
->type
!= EXPR_SYMBOL
)
184 for (i
= 0; i
< ARRAY_SIZE(primitive_funcs
); i
++) {
185 if (sym_name_is(primitive_funcs
[i
], expr
->fn
))
192 static void db_inc(struct expression
*expr
, int param
, char *key
, char *value
)
194 if (is_inc_dec_primitive(expr
))
196 db_inc_dec(expr
, param
, key
, value
, ATOMIC_INC
);
199 static void db_dec(struct expression
*expr
, int param
, char *key
, char *value
)
201 if (is_inc_dec_primitive(expr
))
203 db_inc_dec(expr
, param
, key
, value
, ATOMIC_DEC
);
206 static void match_atomic_inc(const char *fn
, struct expression
*expr
, void *_unused
)
208 db_inc_dec(expr
, 0, "$->counter", "", ATOMIC_INC
);
211 static void match_atomic_dec(const char *fn
, struct expression
*expr
, void *_unused
)
213 db_inc_dec(expr
, 0, "$->counter", "", ATOMIC_DEC
);
216 static void match_atomic_add(const char *fn
, struct expression
*expr
, void *_unused
)
218 struct expression
*amount
;
221 amount
= get_argument_from_call_expr(expr
->args
, 0);
222 if (get_implied_value(amount
, &sval
) && sval_is_negative(sval
)) {
223 db_inc_dec(expr
, 1, "$->counter", "", ATOMIC_DEC
);
227 db_inc_dec(expr
, 1, "$->counter", "", ATOMIC_INC
);
230 static void match_atomic_sub(const char *fn
, struct expression
*expr
, void *_unused
)
232 db_inc_dec(expr
, 1, "$->counter", "", ATOMIC_DEC
);
235 static void refcount_inc(const char *fn
, struct expression
*expr
, void *param
)
237 db_inc_dec(expr
, PTR_INT(param
), "$->ref.counter", "", ATOMIC_INC
);
240 static void refcount_dec(const char *fn
, struct expression
*expr
, void *param
)
242 db_inc_dec(expr
, PTR_INT(param
), "$->ref.counter", "", ATOMIC_DEC
);
245 static void pm_runtime_get_sync(const char *fn
, struct expression
*expr
, void *param
)
247 db_inc_dec(expr
, PTR_INT(param
), "$->power.usage_count.counter", "", ATOMIC_INC
);
250 static void match_implies_inc(const char *fn
, struct expression
*call_expr
,
251 struct expression
*assign_expr
, void *param
)
253 db_inc_dec(call_expr
, PTR_INT(param
), "$->ref.counter", "", ATOMIC_INC
);
256 static void match_implies_atomic_dec(const char *fn
, struct expression
*call_expr
,
257 struct expression
*assign_expr
, void *param
)
259 db_inc_dec(call_expr
, PTR_INT(param
), "$->counter", "", ATOMIC_DEC
);
262 static void match_return_info(int return_id
, char *return_ranges
, struct expression
*expr
)
265 const char *param_name
;
268 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
269 if (sm
->state
!= &inc
&&
272 if (parent_is_gone_var_sym(sm
->name
, sm
->sym
))
274 param
= get_param_num_from_sym(sm
->sym
);
277 param_name
= get_param_name(sm
);
280 sql_insert_return_states(return_id
, return_ranges
,
281 (sm
->state
== &inc
) ? ATOMIC_INC
: ATOMIC_DEC
,
282 param
, param_name
, "");
283 } END_FOR_EACH_SM(sm
);
287 EMPTY
, NEGATIVE
, ZERO
, POSITIVE
, NUM_BUCKETS
290 static int success_fail_positive(struct range_list
*rl
)
295 if (sval_is_negative(rl_min(rl
)))
298 if (rl_min(rl
).value
== 0)
304 static void check_counter(const char *name
, struct symbol
*sym
)
306 struct range_list
*inc_lines
= NULL
;
307 struct range_list
*dec_lines
= NULL
;
308 int inc_buckets
[NUM_BUCKETS
] = {};
309 int dec_buckets
[NUM_BUCKETS
] = {};
310 struct stree
*stree
, *orig_stree
;
311 struct smatch_state
*state
;
312 struct sm_state
*return_sm
;
314 sval_t line
= sval_type_val(&int_ctype
, 0);
317 FOR_EACH_PTR(get_all_return_strees(), stree
) {
318 orig_stree
= __swap_cur_stree(stree
);
320 return_sm
= get_sm_state(RETURN_ID
, "return_ranges", NULL
);
323 line
.value
= return_sm
->line
;
325 if (get_state_stree(start_states
, my_id
, name
, sym
) == &inc
)
328 if (parent_is_gone_var_sym(name
, sym
))
331 sm
= get_sm_state(my_id
, name
, sym
);
335 state
= &start_state
;
339 state
!= &start_state
)
342 bucket
= success_fail_positive(estate_rl(return_sm
->state
));
345 add_range(&inc_lines
, line
, line
);
346 inc_buckets
[bucket
] = true;
348 if (state
== &dec
|| state
== &start_state
) {
349 add_range(&dec_lines
, line
, line
);
350 dec_buckets
[bucket
] = true;
353 __swap_cur_stree(orig_stree
);
354 } END_FOR_EACH_PTR(stree
);
356 if (inc_buckets
[NEGATIVE
] &&
358 // sm_warning("XXX '%s' not decremented on lines: %s.", name, show_rl(inc_lines));
363 static void match_check_missed(struct symbol
*sym
)
367 FOR_EACH_MY_SM(my_id
, get_all_return_states(), sm
) {
368 check_counter(sm
->name
, sm
->sym
);
369 } END_FOR_EACH_SM(sm
);
372 int on_atomic_dec_path(void)
376 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
377 if (sm
->state
== &dec
)
379 } END_FOR_EACH_SM(sm
);
384 int was_inced(const char *name
, struct symbol
*sym
)
386 return get_state(my_id
, name
, sym
) == &inc
;
389 static void match_save_states(struct expression
*expr
)
391 push_stree(&saved_stack
, start_states
);
395 static void match_restore_states(struct expression
*expr
)
397 start_states
= pop_stree(&saved_stack
);
400 static void match_after_func(struct symbol
*sym
)
402 free_stree(&start_states
);
405 void check_atomic_inc_dec(int id
)
409 if (option_project
!= PROJ_KERNEL
)
412 add_unmatched_state_hook(my_id
, &unmatched_state
);
414 add_split_return_callback(match_return_info
);
415 select_return_states_hook(ATOMIC_INC
, &db_inc
);
416 select_return_states_hook(ATOMIC_DEC
, &db_dec
);
418 add_function_hook("atomic_inc_return", &match_atomic_inc
, NULL
);
419 add_function_hook("atomic_add_return", &match_atomic_add
, NULL
);
420 add_function_hook("atomic_sub_return", &match_atomic_sub
, NULL
);
421 add_function_hook("atomic_sub_and_test", &match_atomic_sub
, NULL
);
422 add_function_hook("atomic_long_sub_and_test", &match_atomic_sub
, NULL
);
423 add_function_hook("atomic64_sub_and_test", &match_atomic_sub
, NULL
);
424 add_function_hook("atomic_dec_and_test", &match_atomic_dec
, NULL
);
425 add_function_hook("atomic_long_dec_and_test", &match_atomic_dec
, NULL
);
426 add_function_hook("atomic64_dec_and_test", &match_atomic_dec
, NULL
);
427 add_function_hook("_atomic_dec_and_lock", &match_atomic_dec
, NULL
);
428 add_function_hook("atomic_dec", &match_atomic_dec
, NULL
);
429 add_function_hook("atomic_dec_return", &match_atomic_dec
, NULL
);
430 add_function_hook("atomic_long_inc", &match_atomic_inc
, NULL
);
431 add_function_hook("atomic_long_dec", &match_atomic_dec
, NULL
);
432 add_function_hook("atomic_inc", &match_atomic_inc
, NULL
);
433 add_function_hook("atomic_sub", &match_atomic_sub
, NULL
);
435 add_function_hook("refcount_inc", &refcount_inc
, INT_PTR(0));
436 add_function_hook("refcount_dec", &refcount_dec
, INT_PTR(0));
437 add_function_hook("refcount_add", &refcount_inc
, INT_PTR(1));
439 return_implies_state("refcount_add_not_zero", 1, 1, &match_implies_inc
, INT_PTR(1));
440 return_implies_state("refcount_inc_not_zero", 1, 1, &match_implies_inc
, INT_PTR(0));
442 return_implies_state("atomic_dec_if_positive", 0, INT_MAX
, &match_implies_atomic_dec
, INT_PTR(0));
444 add_function_hook("refcount_sub_and_test", &refcount_dec
, INT_PTR(1));
445 add_function_hook("refcount_dec_and_test", &refcount_dec
, INT_PTR(0));
447 add_function_hook("pm_runtime_get_sync", &pm_runtime_get_sync
, INT_PTR(0));
449 add_hook(&match_check_missed
, END_FUNC_HOOK
);
451 add_hook(&match_after_func
, AFTER_FUNC_HOOK
);
452 add_hook(&match_save_states
, INLINE_FN_START
);
453 add_hook(&match_restore_states
, INLINE_FN_END
);