implied: try again in get_tf_stacks_from_pool()
[smatch.git] / smatch_refcount_info.c
blob585dba46a9ca192cfc43df04ae8383a9964cea89
1 /*
2 * Copyright (C) 2021 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
19 * This is basically a copy for check_preempt_info.c but for refcount instead.
22 #include "smatch.h"
23 #include "smatch_extra.h"
24 #include "smatch_slist.h"
26 static int my_id;
28 STATE(init);
29 STATE(inc);
30 STATE(dec);
31 STATE(ignore);
32 STATE(gone);
34 struct ref_func_info {
35 const char *name;
36 int type;
37 int param;
38 const char *key;
39 const sval_t *implies_start, *implies_end;
40 param_key_hook *call_back;
43 static void match_atomic_add(struct expression *expr, const char *name, struct symbol *sym, void *_unused);
45 static struct ref_func_info func_table[] = {
46 { "atomic_inc", REFCOUNT_INC, 0, "$->counter" },
47 { "atomic_long_inc", REFCOUNT_INC, 0, "$->counter" },
48 { "atomic64_inc", REFCOUNT_INC, 0, "$->counter" },
50 { "atomic_inc_return", REFCOUNT_INC, 0, "$->counter" },
51 { "atomic_long_inc_return", REFCOUNT_INC, 0, "$->counter" },
52 { "atomic64_return", REFCOUNT_INC, 0, "$->counter" },
54 { "atomic_add_return", REFCOUNT_INC, 1, "$->counter", NULL, NULL, match_atomic_add },
55 { "atomic_long_add_return", REFCOUNT_INC, 1, "$->counter", NULL, NULL, match_atomic_add },
56 { "atomic64_add_return", REFCOUNT_INC, 1, "$->counter", NULL, NULL, match_atomic_add },
58 { "atomic64_inc_not_zero", REFCOUNT_INC, 0, "$->counter", &bool_true, &bool_true},
59 { "atomic64_fetch_add_unless", REFCOUNT_INC, 0, "$->counter", &bool_true, &bool_true},
60 { "atomic64_add_unless", REFCOUNT_INC, 0, "$->counter", &bool_true, &bool_true},
61 { "atomic64_add_unless_negative", REFCOUNT_INC, 0, "$->counter", &bool_true, &bool_true},
62 { "atomic64_add_unless_positive", REFCOUNT_INC, 0, "$->counter", &bool_true, &bool_true},
64 // atomic64_dec_if_positive
65 // atomic64_dec_unless_positive
67 { "atomic_dec", REFCOUNT_DEC, 0, "$->counter" },
68 { "atomic_long_dec", REFCOUNT_DEC, 0, "$->counter" },
69 { "atomic64_dec", REFCOUNT_DEC, 0, "$->counter" },
71 { "atomic_dec_return", REFCOUNT_DEC, 0, "$->counter" },
72 { "atomic_long_dec_return", REFCOUNT_DEC, 0, "$->counter" },
73 { "atomic64_dec_return", REFCOUNT_DEC, 0, "$->counter" },
75 { "atomic_dec_and_test", REFCOUNT_DEC, 0, "$->counter" },
76 { "atomic_long_dec_and_test", REFCOUNT_DEC, 0, "$->counter" },
77 { "atomic64_dec_and_test", REFCOUNT_DEC, 0, "$->counter" },
79 { "_atomic_dec_and_lock", REFCOUNT_DEC, 0, "$->counter" },
81 { "atomic_sub", REFCOUNT_DEC, 1, "$->counter" },
82 { "atomic_long_sub", REFCOUNT_DEC, 1, "$->counter" },
83 { "atomic64_sub", REFCOUNT_DEC, 1, "$->counter" },
85 { "atomic_sub_return", REFCOUNT_DEC, 1, "$->counter" },
86 { "atomic_long_sub_return", REFCOUNT_DEC, 1, "$->counter" },
87 { "atomic64_sub_return", REFCOUNT_DEC, 1, "$->counter" },
89 { "atomic_sub_and_test", REFCOUNT_DEC, 1, "$->counter" },
90 { "atomic_long_sub_and_test", REFCOUNT_DEC, 1, "$->counter" },
91 { "atomic64_sub_and_test", REFCOUNT_DEC, 1, "$->counter" },
93 { "register_device", REFCOUNT_INIT, 0, "$->kobj.kset->kobj.kref.refcount.refs.counter", &err_min, &err_max},
94 { "register_device", REFCOUNT_INC, 0, "$->kobj.kset->kobj.kref.refcount.refs.counter", &int_zero, &int_zero},
95 { "device_add", REFCOUNT_INC, 0, "$->kobj.kset->kobj.kref.refcount.refs.counter", &int_zero, &int_zero},
97 { "refcount_set", REFCOUNT_INIT, 0, "$->refs.counter" },
99 { "refcount_inc", REFCOUNT_INC, 0, "$->refs.counter" },
100 { "refcount_dec", REFCOUNT_DEC, 0, "$->refs.counter" },
101 { "refcount_dec_and_test", REFCOUNT_DEC, 0, "$->refs.counter" },
102 { "__refcount_dec_and_test", REFCOUNT_DEC, 0, "$->refs.counter" },
103 { "refcount_sub_and_test", REFCOUNT_DEC, 1, "$->refs.counter" },
104 { "__refcount_sub_and_test", REFCOUNT_DEC, 1, "$->refs.counter" },
105 { "refcount_add", REFCOUNT_INC, 1, "$->refs.counter" },
106 { "refcount_sub_and_test", REFCOUNT_DEC, 1, "$->refs.counter" },
108 { "pm_runtime_get_sync", REFCOUNT_INC, 0, "$->power.usage_count.counter" },
110 { "refcount_inc_not_zero", REFCOUNT_INC, 0, "$->refs.counter", &int_one, &int_one},
111 { "refcount_add_not_zero", REFCOUNT_INC, 1, "$->refs.counter", &int_one, &int_one},
113 { "atomic_dec_if_positive", REFCOUNT_DEC, 0, "$->counter", &int_zero, &int_max},
114 { "atomic64_dec_if_positive", REFCOUNT_DEC, 0, "$->counter", &int_zero, &int_max},
116 { "of_node_get", REFCOUNT_INC, 0, "$->kobj.kref.refcount.refs.counter" },
117 { "of_node_put", REFCOUNT_DEC, 0, "$->kobj.kref.refcount.refs.counter" },
118 { "of_get_parent", REFCOUNT_INC, -1, "$->kobj.kref.refcount.refs.counter" },
119 { "of_clk_del_provider", REFCOUNT_DEC, 0, "$->kobj.kref.refcount.refs.counter" },
121 { "kfree_skb", REFCOUNT_DEC, 0, "$->users.refs.counter" },
123 { "pci_get_dev_by_id", REFCOUNT_INC, -1, "$->dev.kobj.kref.refcount.refs.counter" },
124 { "pci_get_dev_by_id", REFCOUNT_DEC, 1, "$->dev.kobj.kref.refcount.refs.counter" },
126 { "fget", REFCOUNT_INC, -1, "$->f_count.counter", &valid_ptr_min_sval, &valid_ptr_max_sval },
127 { "sockfd_lookup", REFCOUNT_INC, -1, "$->file->f_count.counter", &valid_ptr_min_sval, &valid_ptr_max_sval },
128 { "skb_get", REFCOUNT_INC, 0, "$->users.refs.counter", },
129 // { "fsnotify_put_mark", REFCOUNT_DEC, 0, "$->refcnt.refs.counter" },
131 { "dma_buf_put", REFCOUNT_DEC, 0, "$->file->f_count.counter" },
133 { "xe_device_mem_access_get", REFCOUNT_INC, 0, "$->mem_access.ref.counter", },
134 { "xe_device_mem_access_put", REFCOUNT_DEC, 0, "$->mem_access.ref.counter", },
137 static struct smatch_state *unmatched_state(struct sm_state *sm)
139 if (parent_is_null_var_sym(sm->name, sm->sym))
140 return &gone;
141 return &undefined;
144 static struct smatch_state *merge_states(struct smatch_state *s1, struct smatch_state *s2)
146 if (s1 == &gone)
147 return s2;
148 if (s2 == &gone)
149 return s1;
150 return &merged;
153 static struct name_sym_fn_list *init_hooks, *inc_hooks, *dec_hooks;
155 void add_refcount_init_hook(name_sym_hook *hook)
157 add_ptr_list(&init_hooks, hook);
160 void add_refcount_inc_hook(name_sym_hook *hook)
162 add_ptr_list(&inc_hooks, hook);
165 void add_refcount_dec_hook(name_sym_hook *hook)
167 add_ptr_list(&dec_hooks, hook);
170 void call_hooks(struct name_sym_fn_list *hooks, struct expression *expr,
171 const char *name, struct symbol *sym)
173 name_sym_hook *hook;
175 FOR_EACH_PTR(hooks, hook) {
176 hook(expr, name, sym);
177 } END_FOR_EACH_PTR(hook);
180 static void do_init(struct expression *expr, const char *name, struct symbol *sym)
182 struct smatch_state *orig;
184 call_hooks(init_hooks, expr, name, sym);
186 orig = get_state(my_id, name, sym);
187 if (orig) {
188 set_state(my_id, name, sym, &ignore);
189 return;
192 set_state(my_id, name, sym, &init);
195 static void do_inc(struct expression *expr, const char *name, struct symbol *sym)
197 struct smatch_state *orig;
199 call_hooks(inc_hooks, expr, name, sym);
201 orig = get_state(my_id, name, sym);
202 if (orig) {
203 set_state(my_id, name, sym, &ignore);
204 return;
207 set_state(my_id, name, sym, &inc);
210 static void do_dec(struct expression *expr, const char *name, struct symbol *sym)
212 struct smatch_state *orig;
214 call_hooks(dec_hooks, expr, name, sym);
216 orig = get_state(my_id, name, sym);
217 if (orig) {
218 set_state(my_id, name, sym, &ignore);
219 return;
222 set_state(my_id, name, sym, &dec);
225 static bool is_refcount_primitive(struct expression *expr)
227 int i;
229 while (expr->type == EXPR_ASSIGNMENT)
230 expr = strip_expr(expr->right);
231 if (expr->type != EXPR_CALL)
232 return false;
234 if (expr->fn->type != EXPR_SYMBOL)
235 return false;
237 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
238 if (sym_name_is(func_table[i].name, expr->fn))
239 return true;
242 return false;
245 static void match_atomic_add(struct expression *expr, const char *name, struct symbol *sym, void *_unused)
247 struct expression *amount;
248 sval_t sval;
250 amount = get_argument_from_call_expr(expr->args, 0);
251 if (!get_implied_value(amount, &sval)) {
252 do_inc(expr, name, sym);
253 return;
256 if (sval.value == 0) {
257 set_state(my_id, name, sym, &ignore);
258 return;
261 if (sval_is_positive(sval))
262 do_inc(expr, name, sym);
263 else
264 do_dec(expr, name, sym);
267 static void refcount_init(struct expression *expr, const char *name, struct symbol *sym, void *data)
269 if (!data && is_refcount_primitive(expr))
270 return;
271 do_init(expr, name, sym);
274 static void refcount_inc(struct expression *expr, const char *name, struct symbol *sym, void *data)
276 if (!data && is_refcount_primitive(expr))
277 return;
278 do_inc(expr, name, sym);
281 static void refcount_dec(struct expression *expr, const char *name, struct symbol *sym, void *data)
283 if (!data && is_refcount_primitive(expr))
284 return;
285 do_dec(expr, name, sym);
288 static void match_return_info(int return_id, char *return_ranges,
289 struct expression *returned_expr,
290 int param,
291 const char *printed_name,
292 struct sm_state *sm)
294 struct smatch_state *state;
295 int type;
297 if (param == -1 && return_ranges && strcmp(return_ranges, "0") == 0)
298 return;
300 state = sm->state;
301 if (state == &init)
302 type = REFCOUNT_INIT;
303 else if (state == &inc)
304 type = REFCOUNT_INC;
305 else if (state == &dec)
306 type = REFCOUNT_DEC;
307 else
308 return;
310 sql_insert_return_states(return_id, return_ranges, type, param, printed_name, "");
313 static void match_asm(struct statement *stmt)
315 struct expression *expr;
316 struct asm_operand *op;
317 struct symbol *sym;
318 bool inc = false;
319 char *macro;
320 char *name;
322 macro = get_macro_name(stmt->pos);
323 if (!macro)
324 return;
326 if (strcmp(macro, "this_cpu_inc") == 0)
327 inc = true;
328 else if (strcmp(macro, "this_cpu_dec") != 0)
329 return;
331 op = first_ptr_list((struct ptr_list *)stmt->asm_outputs);
332 if (!op)
333 return;
335 expr = strip_expr(op->expr);
336 name = expr_to_var_sym(expr, &sym);
337 if (!name || !sym)
338 return;
340 if (inc)
341 do_inc(expr, name, sym);
342 else
343 do_dec(expr, name, sym);
346 void register_refcount_info(int id)
348 struct ref_func_info *info;
349 param_key_hook *cb;
350 int i;
352 my_id = id;
354 if (option_project != PROJ_KERNEL)
355 return;
357 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
358 info = &func_table[i];
360 if (info->call_back)
361 cb = info->call_back;
362 else if (info->type == REFCOUNT_INIT)
363 cb = refcount_init;
364 else if (info->type == REFCOUNT_INC)
365 cb = refcount_inc;
366 else
367 cb = refcount_dec;
369 if (info->implies_start) {
370 return_implies_param_key(info->name,
371 *info->implies_start, *info->implies_end,
372 cb, info->param, info->key, info);
373 } else {
374 add_function_param_key_hook_late(info->name, cb,
375 info->param, info->key, info);
379 add_merge_hook(my_id, &merge_states);
380 add_return_info_callback(my_id, &match_return_info);
381 add_hook(match_asm, ASM_HOOK);
383 select_return_param_key(REFCOUNT_INC, &refcount_inc);
384 select_return_param_key(REFCOUNT_DEC, &refcount_dec);
386 add_unmatched_state_hook(my_id, &unmatched_state);