flow: handle struct assignments later
[smatch.git] / check_refcount_inconsistent_returns.c
blob507ebfaa0d68e0d2bb68ea36e40785aaab27c252
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
18 #include "smatch.h"
19 #include "smatch_extra.h"
20 #include "smatch_slist.h"
22 static int my_id;
24 STATE(inc);
25 STATE(dec);
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);
31 return;
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;
51 struct sm_state *sm;
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())
60 goto swap_stree;
62 if (parent_is_gone_var_sym(name, sym))
63 goto swap_stree;
65 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
66 if (!return_sm)
67 goto swap_stree;
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++;
75 else
76 success_path_unknown++;
77 goto swap_stree;
80 if (!sm)
81 goto swap_stree;
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 */
88 swap_stree:
89 __swap_cur_stree(orig);
90 } END_FOR_EACH_PTR(stree);
92 if (!success_path_increments || success_path_unknown)
93 return;
94 if (!dec_lines || !inc_lines)
95 return;
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;
116 struct sm_state *sm;
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())
124 goto swap_stree;
126 if (parent_is_gone_var_sym(name, sym))
127 goto swap_stree;
129 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
130 if (!return_sm)
131 goto swap_stree;
132 line.value = return_sm->line;
134 sm = get_sm_state(my_id, name, sym);
135 if (!sm)
136 goto swap_stree;
138 if (slist_has_state(sm->possible, &dec)) {
139 has_dec = true;
140 goto swap_stree;
143 if (success_fail_return(estate_rl(return_sm->state)) == RET_FAIL &&
144 sm->state == &inc)
145 add_range(&inc_lines, line, line);
146 swap_stree:
147 __swap_cur_stree(orig);
148 } END_FOR_EACH_PTR(stree);
150 if (!has_dec || !inc_lines)
151 return;
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)
167 my_id = id;
169 if (option_project != PROJ_KERNEL)
170 return;
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);