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.
23 #include "smatch_extra.h"
24 #include "smatch_slist.h"
34 struct ref_func_info
{
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
))
144 static struct smatch_state
*merge_states(struct smatch_state
*s1
, struct smatch_state
*s2
)
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
)
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
);
188 set_state(my_id
, name
, sym
, &ignore
);
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
);
203 set_state(my_id
, name
, sym
, &ignore
);
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
);
218 set_state(my_id
, name
, sym
, &ignore
);
222 set_state(my_id
, name
, sym
, &dec
);
225 static bool is_refcount_primitive(struct expression
*expr
)
229 while (expr
->type
== EXPR_ASSIGNMENT
)
230 expr
= strip_expr(expr
->right
);
231 if (expr
->type
!= EXPR_CALL
)
234 if (expr
->fn
->type
!= EXPR_SYMBOL
)
237 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
238 if (sym_name_is(func_table
[i
].name
, expr
->fn
))
245 static void match_atomic_add(struct expression
*expr
, const char *name
, struct symbol
*sym
, void *_unused
)
247 struct expression
*amount
;
250 amount
= get_argument_from_call_expr(expr
->args
, 0);
251 if (!get_implied_value(amount
, &sval
)) {
252 do_inc(expr
, name
, sym
);
256 if (sval
.value
== 0) {
257 set_state(my_id
, name
, sym
, &ignore
);
261 if (sval_is_positive(sval
))
262 do_inc(expr
, name
, sym
);
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
))
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
))
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
))
285 do_dec(expr
, name
, sym
);
288 static void match_return_info(int return_id
, char *return_ranges
,
289 struct expression
*returned_expr
,
291 const char *printed_name
,
294 struct smatch_state
*state
;
297 if (param
== -1 && return_ranges
&& strcmp(return_ranges
, "0") == 0)
302 type
= REFCOUNT_INIT
;
303 else if (state
== &inc
)
305 else if (state
== &dec
)
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
;
322 macro
= get_macro_name(stmt
->pos
);
326 if (strcmp(macro
, "this_cpu_inc") == 0)
328 else if (strcmp(macro
, "this_cpu_dec") != 0)
331 op
= first_ptr_list((struct ptr_list
*)stmt
->asm_outputs
);
335 expr
= strip_expr(op
->expr
);
336 name
= expr_to_var_sym(expr
, &sym
);
341 do_inc(expr
, name
, sym
);
343 do_dec(expr
, name
, sym
);
346 void register_refcount_info(int id
)
348 struct ref_func_info
*info
;
354 if (option_project
!= PROJ_KERNEL
)
357 for (i
= 0; i
< ARRAY_SIZE(func_table
); i
++) {
358 info
= &func_table
[i
];
361 cb
= info
->call_back
;
362 else if (info
->type
== REFCOUNT_INIT
)
364 else if (info
->type
== REFCOUNT_INC
)
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
);
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
);