function_hooks: fake an assignment when functions return "0-s32max[$0->bar]"
[smatch.git] / check_atomic_inc_dec.c
blobcd3fe5ed13ceb85c207b4408f88e51f95e206351
1 /*
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
18 #include "smatch.h"
19 #include "smatch_extra.h"
20 #include "smatch_slist.h"
22 static int my_id;
24 STATE(inc);
25 STATE(orig);
26 STATE(dec);
28 static void db_inc_dec(struct expression *expr, int param, const char *key, const char *value, int inc_dec)
30 struct smatch_state *start_state;
31 struct expression *arg;
32 char *name;
33 struct symbol *sym;
35 while (expr->type == EXPR_ASSIGNMENT)
36 expr = strip_expr(expr->right);
37 if (expr->type != EXPR_CALL)
38 return;
40 arg = get_argument_from_call_expr(expr->args, param);
41 if (!arg)
42 return;
44 name = get_variable_from_key(arg, key, &sym);
45 if (!name || !sym)
46 goto free;
48 start_state = get_state(my_id, name, sym);
50 if (inc_dec == ATOMIC_INC) {
51 // if (start_state == &inc)
52 // sm_msg("error: XXX double increment '%s'", name);
53 set_state(my_id, name, sym, &inc);
54 } else {
55 // if (start_state == &dec)
56 // sm_msg("error: XXX double decrement '%s'", name);
57 if (start_state == &inc)
58 set_state(my_id, name, sym, &orig);
59 else
60 set_state(my_id, name, sym, &dec);
63 free:
64 free_string(name);
67 static void db_inc(struct expression *expr, int param, char *key, char *value)
69 db_inc_dec(expr, param, key, value, ATOMIC_INC);
72 static void db_dec(struct expression *expr, int param, char *key, char *value)
74 db_inc_dec(expr, param, key, value, ATOMIC_DEC);
77 static void match_atomic_inc(const char *fn, struct expression *expr, void *_unused)
79 db_inc_dec(expr, 0, "$->counter", "", ATOMIC_INC);
82 static void match_atomic_dec(const char *fn, struct expression *expr, void *_unused)
84 db_inc_dec(expr, 0, "$->counter", "", ATOMIC_DEC);
87 static void match_atomic_add(const char *fn, struct expression *expr, void *_unused)
89 struct expression *amount;
90 sval_t sval;
92 amount = get_argument_from_call_expr(expr->args, 0);
93 if (get_implied_value(amount, &sval) && sval_is_negative(sval)) {
94 db_inc_dec(expr, 1, "$->counter", "", ATOMIC_DEC);
95 return;
98 db_inc_dec(expr, 1, "$->counter", "", ATOMIC_INC);
101 static void match_atomic_sub(const char *fn, struct expression *expr, void *_unused)
103 db_inc_dec(expr, 1, "$->counter", "", ATOMIC_DEC);
106 static void refcount_inc(const char *fn, struct expression *expr, void *param)
108 db_inc_dec(expr, PTR_INT(param), "$->ref.counter", "", ATOMIC_INC);
111 static void refcount_dec(const char *fn, struct expression *expr, void *param)
113 db_inc_dec(expr, PTR_INT(param), "$->ref.counter", "", ATOMIC_DEC);
116 static void match_return_info(int return_id, char *return_ranges, struct expression *expr)
118 struct sm_state *sm;
119 const char *param_name;
120 int param;
122 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
123 if (sm->state != &inc &&
124 sm->state != &dec)
125 continue;
126 param = get_param_num_from_sym(sm->sym);
127 if (param < 0)
128 continue;
129 param_name = get_param_name(sm);
130 if (!param_name)
131 continue;
132 sql_insert_return_states(return_id, return_ranges,
133 (sm->state == &inc) ? ATOMIC_INC : ATOMIC_DEC,
134 param, param_name, "");
135 } END_FOR_EACH_SM(sm);
138 enum {
139 NEGATIVE, ZERO, POSITIVE,
142 static int success_fail_positive(struct range_list *rl)
144 if (!rl)
145 return ZERO;
147 if (sval_is_negative(rl_min(rl)))
148 return NEGATIVE;
150 if (rl_min(rl).value == 0)
151 return ZERO;
153 return POSITIVE;
156 static void check_counter(const char *name, struct symbol *sym)
158 struct range_list *inc_lines = NULL;
159 struct range_list *dec_lines = NULL;
160 int inc_buckets[3] = {};
161 struct stree *stree;
162 struct sm_state *return_sm;
163 struct sm_state *sm;
164 sval_t line = sval_type_val(&int_ctype, 0);
166 FOR_EACH_PTR(get_all_return_strees(), stree) {
167 return_sm = get_sm_state_stree(stree, RETURN_ID, "return_ranges", NULL);
168 if (!return_sm)
169 continue;
170 line.value = return_sm->line;
172 sm = get_sm_state_stree(stree, my_id, name, sym);
173 if (!sm)
174 continue;
176 if (sm->state != &inc && sm->state != &dec)
177 continue;
179 if (sm->state == &inc) {
180 add_range(&inc_lines, line, line);
181 inc_buckets[success_fail_positive(estate_rl(return_sm->state))] = 1;
183 if (sm->state == &dec)
184 add_range(&dec_lines, line, line);
185 } END_FOR_EACH_PTR(stree);
187 if (inc_buckets[NEGATIVE] &&
188 inc_buckets[ZERO]) {
189 // sm_msg("warn: XXX '%s' not decremented on lines: %s.", name, show_rl(inc_lines));
194 static void match_check_missed(struct symbol *sym)
196 struct sm_state *sm;
198 FOR_EACH_MY_SM(my_id, get_all_return_states(), sm) {
199 check_counter(sm->name, sm->sym);
200 } END_FOR_EACH_SM(sm);
203 int on_atomic_dec_path(void)
205 struct sm_state *sm;
207 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
208 if (sm->state == &dec)
209 return 1;
210 } END_FOR_EACH_SM(sm);
212 return 0;
215 int was_inced(const char *name, struct symbol *sym)
217 return get_state(my_id, name, sym) == &inc;
220 void check_atomic_inc_dec(int id)
222 my_id = id;
224 if (option_project != PROJ_KERNEL)
225 return;
227 select_return_states_hook(ATOMIC_INC, &db_inc);
228 select_return_states_hook(ATOMIC_DEC, &db_dec);
229 add_function_hook("atomic_inc_return", &match_atomic_inc, NULL);
230 add_function_hook("atomic_add_return", &match_atomic_add, NULL);
231 add_function_hook("atomic_sub_return", &match_atomic_sub, NULL);
232 add_function_hook("atomic_sub_and_test", &match_atomic_sub, NULL);
233 add_function_hook("atomic_dec_and_test", &match_atomic_dec, NULL);
234 add_function_hook("atomic_dec", &match_atomic_dec, NULL);
235 add_function_hook("atomic_long_inc", &match_atomic_inc, NULL);
236 add_function_hook("atomic_long_dec", &match_atomic_dec, NULL);
237 add_function_hook("atomic_inc", &match_atomic_inc, NULL);
238 add_function_hook("atomic_sub", &match_atomic_sub, NULL);
239 add_split_return_callback(match_return_info);
241 add_function_hook("refcount_add_not_zero", &refcount_inc, INT_PTR(1));
242 add_function_hook("refcount_inc_not_zero", &refcount_inc, INT_PTR(0));
243 add_function_hook("refcount_sub_and_test", &refcount_dec, INT_PTR(1));
245 add_hook(&match_check_missed, END_FUNC_HOOK);