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 #include "smatch_extra.h"
20 #include "smatch_slist.h"
27 static void match_inc(struct expression
*expr
, const char *name
, struct symbol
*sym
)
29 if (has_possible_state(my_id
, name
, sym
, &dec
)) {
30 set_state(my_id
, name
, sym
, &undefined
);
34 set_state(my_id
, name
, sym
, &inc
);
37 static void match_dec(struct expression
*expr
, const char *name
, struct symbol
*sym
)
39 set_state(my_id
, name
, sym
, &dec
);
42 // drivers/pcmcia/ds.c:598 pcmcia_device_add() warn: inconsistent refcounting 's->dev.kobj.kref.refcount.refs.counter':
43 // check that parent is gone
45 static void check_count(const char *name
, struct symbol
*sym
)
47 struct stree
*stree
, *orig
;
48 struct sm_state
*return_sm
;
49 struct range_list
*dec_lines
= NULL
;
50 struct range_list
*inc_lines
= NULL
;
52 sval_t line
= sval_type_val(&int_ctype
, 0);
53 int success_path_increments
= 0;
54 int success_path_unknown
= 0;
56 FOR_EACH_PTR(get_all_return_strees(), stree
) {
57 orig
= __swap_cur_stree(stree
);
59 if (is_impossible_path())
62 if (parent_is_gone_var_sym(name
, sym
))
65 return_sm
= get_sm_state(RETURN_ID
, "return_ranges", NULL
);
68 line
.value
= return_sm
->line
;
70 sm
= get_sm_state(my_id
, name
, sym
);
72 if (success_fail_return(estate_rl(return_sm
->state
)) == RET_SUCCESS
) {
73 if (sm
&& strcmp(sm
->state
->name
, "inc") == 0)
74 success_path_increments
++;
76 success_path_unknown
++;
83 if (sm
->state
== &inc
)
84 add_range(&inc_lines
, line
, line
);
85 else if (sm
->state
== &dec
)
86 add_range(&dec_lines
, line
, line
); /* dec or &undefined */
89 __swap_cur_stree(orig
);
90 } END_FOR_EACH_PTR(stree
);
92 if (!success_path_increments
|| success_path_unknown
)
94 if (!dec_lines
|| !inc_lines
)
97 sm_warning("inconsistent refcounting '%s':", name
);
98 sm_printf(" inc on: %s\n", show_rl(inc_lines
));
99 sm_printf(" dec on: %s\n", show_rl(dec_lines
));
102 static void process_states(void)
104 struct sm_state
*tmp
;
106 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), tmp
) {
107 check_count(tmp
->name
, tmp
->sym
);
108 } END_FOR_EACH_SM(tmp
);
111 static void warn_on_leaks(const char *name
, struct symbol
*sym
)
113 struct stree
*stree
, *orig
;
114 struct sm_state
*return_sm
;
115 struct range_list
*inc_lines
= NULL
;
117 sval_t line
= sval_type_val(&int_ctype
, 0);
118 bool has_dec
= false;
120 FOR_EACH_PTR(get_all_return_strees(), stree
) {
121 orig
= __swap_cur_stree(stree
);
123 if (is_impossible_path())
126 if (parent_is_gone_var_sym(name
, sym
))
129 return_sm
= get_sm_state(RETURN_ID
, "return_ranges", NULL
);
132 line
.value
= return_sm
->line
;
134 sm
= get_sm_state(my_id
, name
, sym
);
138 if (slist_has_state(sm
->possible
, &dec
)) {
143 if (success_fail_return(estate_rl(return_sm
->state
)) == RET_FAIL
&&
145 add_range(&inc_lines
, line
, line
);
147 __swap_cur_stree(orig
);
148 } END_FOR_EACH_PTR(stree
);
150 if (!has_dec
|| !inc_lines
)
153 sm_warning("refcount leak '%s': lines='%s'", name
, show_rl(inc_lines
));
156 static void process_states2(void)
158 struct sm_state
*tmp
;
160 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), tmp
) {
161 warn_on_leaks(tmp
->name
, tmp
->sym
);
162 } END_FOR_EACH_SM(tmp
);
165 void check_refcount_inconsistent_returns(int id
)
169 if (option_project
!= PROJ_KERNEL
)
172 add_refcount_init_hook(&match_inc
);
173 add_refcount_inc_hook(&match_inc
);
174 add_refcount_dec_hook(&match_dec
);
176 all_return_states_hook(&process_states
);
177 all_return_states_hook(&process_states2
);