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"
33 struct ref_func_info
{
38 const sval_t
*implies_start
, *implies_end
;
42 static void match_atomic_add(const char *fn
, struct expression
*expr
, void *_unused
);
44 static struct ref_func_info func_table
[] = {
45 { "atomic_inc", ATOMIC_INC
, 0, "$->counter" },
46 { "atomic_long_inc", ATOMIC_INC
, 0, "$->counter" },
47 { "atomic64_inc", ATOMIC_INC
, 0, "$->counter" },
49 { "atomic_inc_return", ATOMIC_INC
, 0, "$->counter" },
50 { "atomic_long_inc_return", ATOMIC_INC
, 0, "$->counter" },
51 { "atomic64_return", ATOMIC_INC
, 0, "$->counter" },
53 { "atomic_add_return", ATOMIC_INC
, 1, "$->counter", NULL
, NULL
, match_atomic_add
},
54 { "atomic_long_add_return", ATOMIC_INC
, 1, "$->counter", NULL
, NULL
, match_atomic_add
},
55 { "atomic64_add_return", ATOMIC_INC
, 1, "$->counter", NULL
, NULL
, match_atomic_add
},
57 { "atomic_dec", ATOMIC_DEC
, 0, "$->counter" },
58 { "atomic_long_dec", ATOMIC_DEC
, 0, "$->counter" },
59 { "atomic64_dec", ATOMIC_DEC
, 0, "$->counter" },
61 { "atomic_dec_return", ATOMIC_DEC
, 0, "$->counter" },
62 { "atomic_long_dec_return", ATOMIC_DEC
, 0, "$->counter" },
63 { "atomic64_dec_return", ATOMIC_DEC
, 0, "$->counter" },
65 { "atomic_dec_and_test", ATOMIC_DEC
, 0, "$->counter" },
66 { "atomic_long_dec_and_test", ATOMIC_DEC
, 0, "$->counter" },
67 { "atomic64_dec_and_test", ATOMIC_DEC
, 0, "$->counter" },
69 { "_atomic_dec_and_lock", ATOMIC_DEC
, 0, "$->counter" },
71 { "atomic_sub", ATOMIC_DEC
, 1, "$->counter" },
72 { "atomic_long_sub", ATOMIC_DEC
, 1, "$->counter" },
73 { "atomic64_sub", ATOMIC_DEC
, 1, "$->counter" },
75 { "atomic_sub_return", ATOMIC_DEC
, 1, "$->counter" },
76 { "atomic_long_sub_return", ATOMIC_DEC
, 1, "$->counter" },
77 { "atomic64_sub_return", ATOMIC_DEC
, 1, "$->counter" },
79 { "atomic_sub_and_test", ATOMIC_DEC
, 1, "$->counter" },
80 { "atomic_long_sub_and_test", ATOMIC_DEC
, 1, "$->counter" },
81 { "atomic64_sub_and_test", ATOMIC_DEC
, 1, "$->counter" },
83 { "refcount_inc", ATOMIC_INC
, 0, "$->refs.counter" },
84 { "refcount_dec", ATOMIC_DEC
, 0, "$->refs.counter" },
85 { "refcount_dec_and_test", ATOMIC_DEC
, 0, "$->refs.counter" },
86 { "refcount_add", ATOMIC_INC
, 1, "$->refs.counter" },
87 { "refcount_sub_and_test", ATOMIC_DEC
, 1, "$->refs.counter" },
89 { "pm_runtime_get_sync", ATOMIC_INC
, 0, "$->power.usage_count.counter" },
90 { "of_clk_del_provider", ATOMIC_DEC
, 0, "$->kobj.kref.refcount.refs.counter" },
92 { "refcount_inc_not_zero", ATOMIC_INC
, 0, "$->refs.counter", &int_one
, &int_one
},
93 { "refcount_add_not_zero", ATOMIC_INC
, 1, "$->refs.counter", &int_one
, &int_one
},
95 { "atomic_dec_if_positive", ATOMIC_DEC
, 0, "$->counter", &int_zero
, &int_max
},
96 { "atomic64_dec_if_positive", ATOMIC_DEC
, 0, "$->counter", &int_zero
, &int_max
},
98 { "of_node_get", ATOMIC_INC
, 0, "$->kobj.kref.refcount.refs.counter" },
99 { "of_node_put", ATOMIC_DEC
, 0, "$->kobj.kref.refcount.refs.counter" },
100 { "of_get_parent", ATOMIC_INC
, -1, "$->kobj.kref.refcount.refs.counter" },
101 { "of_clk_del_provider", ATOMIC_DEC
, 0, "$->kobj.kref.refcount.refs.counter" },
103 { "kfree_skb", ATOMIC_DEC
, 0, "$->users.refs.counter" },
106 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
109 * We default to decremented. For example, say we have:
112 * <- p is decreemented.
115 if ((sm
->state
== &dec
) &&
116 parent_is_gone_var_sym(sm
->name
, sm
->sym
))
121 static struct stree
*start_states
;
122 static void set_start_state(const char *name
, struct symbol
*sym
, struct smatch_state
*start
)
124 struct smatch_state
*orig
;
126 orig
= get_state_stree(start_states
, my_id
, name
, sym
);
128 set_state_stree(&start_states
, my_id
, name
, sym
, start
);
129 else if (orig
!= start
)
130 set_state_stree(&start_states
, my_id
, name
, sym
, &undefined
);
133 static struct sm_state
*get_best_match(const char *key
)
136 struct sm_state
*match
;
138 int start_pos
, state_len
, key_len
, chunks
, i
;
140 if (strncmp(key
, "$->", 3) == 0)
143 key_len
= strlen(key
);
145 for (i
= key_len
- 1; i
> 0; i
--) {
146 if (key
[i
] == '>' || key
[i
] == '.')
150 key_len
= strlen(key
);
155 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
156 state_len
= strlen(sm
->name
);
157 if (state_len
< key_len
)
159 start_pos
= state_len
- key_len
;
160 if ((start_pos
== 0 || !isalnum(sm
->name
[start_pos
- 1])) &&
161 strcmp(sm
->name
+ start_pos
, key
) == 0) {
165 } END_FOR_EACH_SM(sm
);
172 static void handle_test_functions(struct expression
*expr
)
174 struct expression
*tmp
;
175 struct statement
*stmt
;
178 if (expr
->type
!= EXPR_CALL
||
179 expr
->fn
->type
!= EXPR_SYMBOL
||
180 !expr
->fn
->symbol_name
)
182 if (!strstr(expr
->fn
->symbol_name
->name
, "test"))
185 while ((tmp
= expr_get_parent_expr(expr
))) {
191 stmt
= expr_get_parent_stmt(expr
);
192 if (!stmt
|| stmt
->type
!= STMT_IF
)
195 set_true_false_states(test_id
, "dec_path", NULL
, &zero_path
, NULL
);
198 static void db_inc_dec(struct expression
*expr
, int param
, const char *key
, int inc_dec
)
200 struct sm_state
*start_sm
;
201 struct expression
*call
, *arg
;
204 bool free_at_end
= true;
207 while (call
&& call
->type
== EXPR_ASSIGNMENT
)
208 call
= strip_expr(call
->right
);
210 if (!call
|| call
->type
!= EXPR_CALL
)
213 handle_test_functions(call
);
216 expr
->type
== EXPR_ASSIGNMENT
&&
218 name
= get_variable_from_key(expr
->left
, key
, &sym
);
221 } else if (param
>= 0) {
222 arg
= get_argument_from_call_expr(call
->args
, param
);
226 name
= get_variable_from_key(arg
, key
, &sym
);
230 name
= alloc_string(key
);
234 start_sm
= get_sm_state(my_id
, name
, sym
);
235 if (!start_sm
&& !sym
&& inc_dec
== ATOMIC_DEC
) {
236 start_sm
= get_best_match(key
);
240 name
= (char *)start_sm
->name
;
245 if (inc_dec
== ATOMIC_INC
) {
247 set_start_state(name
, sym
, &dec
);
248 // set_refcount_inc(name, sym);
249 set_state(my_id
, name
, sym
, &inc
);
251 // set_refcount_dec(name, sym);
253 set_start_state(name
, sym
, &inc
);
255 if (start_sm
&& start_sm
->state
== &inc
)
256 set_state(my_id
, name
, sym
, &start_state
);
258 set_state(my_id
, name
, sym
, &dec
);
266 static bool is_inc_dec_primitive(struct expression
*expr
)
270 while (expr
->type
== EXPR_ASSIGNMENT
)
271 expr
= strip_expr(expr
->right
);
272 if (expr
->type
!= EXPR_CALL
)
275 if (expr
->fn
->type
!= EXPR_SYMBOL
)
278 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
279 if (sym_name_is(func_table
[i
].name
, expr
->fn
))
286 static void db_inc(struct expression
*expr
, int param
, char *key
, char *value
)
288 if (is_inc_dec_primitive(expr
))
290 db_inc_dec(expr
, param
, key
, ATOMIC_INC
);
293 static void db_dec(struct expression
*expr
, int param
, char *key
, char *value
)
295 if (is_inc_dec_primitive(expr
))
297 db_inc_dec(expr
, param
, key
, ATOMIC_DEC
);
300 static void match_atomic_add(const char *fn
, struct expression
*expr
, void *_unused
)
302 struct expression
*amount
;
305 amount
= get_argument_from_call_expr(expr
->args
, 0);
306 if (get_implied_value(amount
, &sval
) && sval_is_negative(sval
)) {
307 db_inc_dec(expr
, 1, "$->counter", ATOMIC_DEC
);
311 db_inc_dec(expr
, 1, "$->counter", ATOMIC_INC
);
314 static void refcount_function(const char *fn
, struct expression
*expr
, void *data
)
316 struct ref_func_info
*info
= data
;
318 db_inc_dec(expr
, info
->param
, info
->key
, info
->type
);
321 static void refcount_implied(const char *fn
, struct expression
*call_expr
,
322 struct expression
*assign_expr
, void *data
)
324 struct ref_func_info
*info
= data
;
326 db_inc_dec(assign_expr
?: call_expr
, info
->param
, info
->key
, info
->type
);
329 static bool is_maybe_dec(struct sm_state
*sm
)
331 if (sm
->state
== &dec
)
333 if (slist_has_state(sm
->possible
, &dec
) &&
334 !slist_has_state(sm
->possible
, &inc
))
339 static void match_return_info(int return_id
, char *return_ranges
, struct expression
*expr
)
342 const char *param_name
;
345 if (is_impossible_path())
348 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
349 if (sm
->state
!= &inc
&& !is_maybe_dec(sm
))
351 if (sm
->state
== get_state_stree(start_states
, my_id
, sm
->name
, sm
->sym
))
353 if (parent_is_null_var_sym(sm
->name
, sm
->sym
))
355 if (sm
->state
== &inc
&&
356 parent_is_free_var_sym(sm
->name
, sm
->sym
))
358 param
= get_param_key_from_sm(sm
, expr
, ¶m_name
);
361 sql_insert_return_states(return_id
, return_ranges
,
362 (sm
->state
== &inc
) ? ATOMIC_INC
: ATOMIC_DEC
,
363 param
, param_name
, "");
364 } END_FOR_EACH_SM(sm
);
368 EMPTY
, NEGATIVE
, ZERO
, POSITIVE
, NUM_BUCKETS
371 static int success_fail_positive(struct range_list
*rl
)
376 if (!is_whole_rl(rl
) && sval_is_negative(rl_min(rl
)))
379 if (rl_min(rl
).value
== 0)
385 static void check_counter(const char *name
, struct symbol
*sym
)
387 struct range_list
*inc_lines
= NULL
;
388 struct range_list
*dec_lines
= NULL
;
389 int inc_buckets
[NUM_BUCKETS
] = {};
390 int dec_buckets
[NUM_BUCKETS
] = {};
391 int start_buckets
[NUM_BUCKETS
] = {};
392 struct stree
*stree
, *orig_stree
;
393 struct smatch_state
*state
;
394 struct sm_state
*return_sm
;
396 sval_t line
= sval_type_val(&int_ctype
, 0);
399 /* don't warn about stuff we can't identify */
403 /* static variable are probably just counters */
404 if (sym
->ctype
.modifiers
& MOD_STATIC
&&
405 !(sym
->ctype
.modifiers
& MOD_TOPLEVEL
))
408 if (strstr(name
, "error") ||
409 strstr(name
, "drop") ||
410 strstr(name
, "xmt_ls_err") ||
411 strstr(name
, "->stats->") ||
412 strstr(name
, "->stats."))
415 if (strstr(name
, "power.usage_count"))
418 FOR_EACH_PTR(get_all_return_strees(), stree
) {
419 orig_stree
= __swap_cur_stree(stree
);
421 if (is_impossible_path())
424 return_sm
= get_sm_state(RETURN_ID
, "return_ranges", NULL
);
427 line
.value
= return_sm
->line
;
429 if (get_state_stree(start_states
, my_id
, name
, sym
) == &inc
)
432 if (parent_is_gone_var_sym(name
, sym
))
435 sm
= get_sm_state(my_id
, name
, sym
);
439 state
= &start_state
;
443 state
!= &start_state
)
446 bucket
= success_fail_positive(estate_rl(return_sm
->state
));
449 add_range(&inc_lines
, line
, line
);
450 inc_buckets
[bucket
]++;
453 add_range(&dec_lines
, line
, line
);
454 dec_buckets
[bucket
]++;
456 if (state
== &start_state
)
457 start_buckets
[bucket
]++;
459 __swap_cur_stree(orig_stree
);
460 } END_FOR_EACH_PTR(stree
);
462 if (inc_buckets
[NEGATIVE
] &&
464 // sm_warning("XXX '%s' not decremented on lines: %s.", name, show_rl(inc_lines));
469 static void match_check_missed(struct symbol
*sym
)
473 FOR_EACH_MY_SM(my_id
, get_all_return_states(), sm
) {
474 check_counter(sm
->name
, sm
->sym
);
475 } END_FOR_EACH_SM(sm
);
478 int on_atomic_dec_path(void)
480 return get_state(test_id
, "dec_path", NULL
) == &zero_path
;
483 int was_inced(const char *name
, struct symbol
*sym
)
485 return get_state(my_id
, name
, sym
) == &inc
;
488 static void match_after_func(struct symbol
*sym
)
490 free_stree(&start_states
);
493 void check_atomic_inc_dec(int id
)
495 struct ref_func_info
*info
;
500 if (option_project
!= PROJ_KERNEL
)
503 add_unmatched_state_hook(my_id
, &unmatched_state
);
505 add_split_return_callback(match_return_info
);
506 select_return_states_hook(ATOMIC_INC
, &db_inc
);
507 select_return_states_hook(ATOMIC_DEC
, &db_dec
);
509 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
510 info
= &func_table
[i
];
512 if (info
->call_back
) {
513 add_function_hook(info
->name
, info
->call_back
, info
);
514 } else if (info
->implies_start
) {
515 return_implies_state_sval(info
->name
,
516 *info
->implies_start
, *info
->implies_end
,
517 &refcount_implied
, info
);
519 add_function_hook(info
->name
, &refcount_function
, info
);
523 add_hook(&match_check_missed
, END_FUNC_HOOK
);
525 add_hook(&match_after_func
, AFTER_FUNC_HOOK
);
526 add_function_data((unsigned long *)&start_states
);
529 void check_atomic_test(int id
)
531 if (option_project
!= PROJ_KERNEL
)