1 /* 2 * Copyright (C) 2016 Oracle. 3 * 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. 8 * 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. 13 * 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 16 */ 17 18 #include "smatch.h" 19 #include "smatch_extra.h" 20 #include "smatch_slist.h" 21 22 static int my_id; 23 24 STATE(inc); 25 STATE(orig); 26 STATE(dec); 27 28 static void db_inc_dec(struct expression *expr, int param, const char *key, const char *value, int inc_dec) 29 { 30 struct smatch_state *start_state; 31 struct expression *arg; 32 char *name; 33 struct symbol *sym; 34 35 while (expr->type == EXPR_ASSIGNMENT) 36 expr = strip_expr(expr->right); 37 if (expr->type != EXPR_CALL) 38 return; 39 40 arg = get_argument_from_call_expr(expr->args, param); 41 if (!arg) 42 return; 43 44 name = get_variable_from_key(arg, key, &sym); 45 if (!name || !sym) 46 goto free; 47 48 start_state = get_state(my_id, name, sym); 49 50 if (inc_dec == ATOMIC_INC) { 51 // if (start_state == &inc) 52 // sm_error("XXX double increment '%s'", name); 53 set_state(my_id, name, sym, &inc); 54 } else { 55 // if (start_state == &dec) 56 // sm_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); 61 } 62 63 free: 64 free_string(name); 65 } 66 67 static void db_inc(struct expression *expr, int param, char *key, char *value) 68 { 69 db_inc_dec(expr, param, key, value, ATOMIC_INC); 70 } 71 72 static void db_dec(struct expression *expr, int param, char *key, char *value) 73 { 74 db_inc_dec(expr, param, key, value, ATOMIC_DEC); 75 } 76 77 static void match_atomic_inc(const char *fn, struct expression *expr, void *_unused) 78 { 79 db_inc_dec(expr, 0, "$->counter", "", ATOMIC_INC); 80 } 81 82 static void match_atomic_dec(const char *fn, struct expression *expr, void *_unused) 83 { 84 db_inc_dec(expr, 0, "$->counter", "", ATOMIC_DEC); 85 } 86 87 static void match_atomic_add(const char *fn, struct expression *expr, void *_unused) 88 { 89 struct expression *amount; 90 sval_t sval; 91 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; 96 } 97 98 db_inc_dec(expr, 1, "$->counter", "", ATOMIC_INC); 99 } 100 101 static void match_atomic_sub(const char *fn, struct expression *expr, void *_unused) 102 { 103 db_inc_dec(expr, 1, "$->counter", "", ATOMIC_DEC); 104 } 105 106 static void refcount_inc(const char *fn, struct expression *expr, void *param) 107 { 108 db_inc_dec(expr, PTR_INT(param), "$->ref.counter", "", ATOMIC_INC); 109 } 110 111 static void refcount_dec(const char *fn, struct expression *expr, void *param) 112 { 113 db_inc_dec(expr, PTR_INT(param), "$->ref.counter", "", ATOMIC_DEC); 114 } 115 116 static void match_return_info(int return_id, char *return_ranges, struct expression *expr) 117 { 118 struct sm_state *sm; 119 const char *param_name; 120 int param; 121 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); 136 } 137 138 enum { 139 NEGATIVE, ZERO, POSITIVE, 140 }; 141 142 static int success_fail_positive(struct range_list *rl) 143 { 144 if (!rl) 145 return ZERO; 146 147 if (sval_is_negative(rl_min(rl))) 148 return NEGATIVE; 149 150 if (rl_min(rl).value == 0) 151 return ZERO; 152 153 return POSITIVE; 154 } 155 156 static void check_counter(const char *name, struct symbol *sym) 157 { 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); 165 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; 171 172 sm = get_sm_state_stree(stree, my_id, name, sym); 173 if (!sm) 174 continue; 175 176 if (sm->state != &inc && sm->state != &dec) 177 continue; 178 179 if (sm->state == &inc) { 180 add_range(&inc_lines, line, line); 181 inc_buckets[success_fail_positive(estate_rl(return_sm->state))] = 1; 182 } 183 if (sm->state == &dec) 184 add_range(&dec_lines, line, line); 185 } END_FOR_EACH_PTR(stree); 186 187 if (inc_buckets[NEGATIVE] && 188 inc_buckets[ZERO]) { 189 // sm_warning("XXX '%s' not decremented on lines: %s.", name, show_rl(inc_lines)); 190 } 191 192 } 193 194 static void match_check_missed(struct symbol *sym) 195 { 196 struct sm_state *sm; 197 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); 201 } 202 203 int on_atomic_dec_path(void) 204 { 205 struct sm_state *sm; 206 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); 211 212 return 0; 213 } 214 215 int was_inced(const char *name, struct symbol *sym) 216 { 217 return get_state(my_id, name, sym) == &inc; 218 } 219 220 void check_atomic_inc_dec(int id) 221 { 222 my_id = id; 223 224 if (option_project != PROJ_KERNEL) 225 return; 226 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_and_lock", &match_atomic_dec, NULL); 235 add_function_hook("atomic_dec", &match_atomic_dec, NULL); 236 add_function_hook("atomic_long_inc", &match_atomic_inc, NULL); 237 add_function_hook("atomic_long_dec", &match_atomic_dec, NULL); 238 add_function_hook("atomic_inc", &match_atomic_inc, NULL); 239 add_function_hook("atomic_sub", &match_atomic_sub, NULL); 240 add_split_return_callback(match_return_info); 241 242 add_function_hook("refcount_add_not_zero", &refcount_inc, INT_PTR(1)); 243 add_function_hook("refcount_inc_not_zero", &refcount_inc, INT_PTR(0)); 244 add_function_hook("refcount_sub_and_test", &refcount_dec, INT_PTR(1)); 245 add_function_hook("refcount_dec_and_test", &refcount_dec, INT_PTR(1)); 246 247 add_hook(&match_check_missed, END_FUNC_HOOK); 248 } 249