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 <ctype.h> 19 20 #include "smatch.h" 21 #include "smatch_extra.h" 22 #include "smatch_slist.h" 23 24 static int my_id; 25 26 STATE(inc); 27 STATE(start_state); 28 STATE(dec); 29 30 static struct smatch_state *unmatched_state(struct sm_state *sm) 31 { 32 /* 33 * We default to decremented. For example, say we have: 34 * if (p) 35 * atomic_dec(p); 36 * <- p is decreemented. 37 * 38 */ 39 if ((sm->state == &dec) && 40 parent_is_gone_var_sym(sm->name, sm->sym)) 41 return sm->state; 42 return &start_state; 43 } 44 45 static struct stree *start_states; 46 static struct stree_stack *saved_stack; 47 static void set_start_state(const char *name, struct symbol *sym, struct smatch_state *start) 48 { 49 struct smatch_state *orig; 50 51 orig = get_state_stree(start_states, my_id, name, sym); 52 if (!orig) 53 set_state_stree(&start_states, my_id, name, sym, start); 54 else if (orig != start) 55 set_state_stree(&start_states, my_id, name, sym, &undefined); 56 } 57 58 static struct sm_state *get_best_match(const char *key) 59 { 60 struct sm_state *sm; 61 struct sm_state *match; 62 int cnt = 0; 63 int start_pos, state_len, key_len, chunks, i; 64 65 if (strncmp(key, "$->", 3) == 0) 66 key += 3; 67 68 key_len = strlen(key); 69 chunks = 0; 70 for (i = key_len - 1; i > 0; i--) { 71 if (key[i] == '>' || key[i] == '.') 72 chunks++; 73 if (chunks == 2) { 74 key += (i + 1); 75 key_len = strlen(key); 76 break; 77 } 78 } 79 80 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 81 state_len = strlen(sm->name); 82 if (state_len < key_len) 83 continue; 84 start_pos = state_len - key_len; 85 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) && 86 strcmp(sm->name + start_pos, key) == 0) { 87 cnt++; 88 match = sm; 89 } 90 } END_FOR_EACH_SM(sm); 91 92 if (cnt == 1) 93 return match; 94 return NULL; 95 } 96 97 static void db_inc_dec(struct expression *expr, int param, const char *key, int inc_dec) 98 { 99 struct sm_state *start_sm; 100 struct expression *arg; 101 char *name; 102 struct symbol *sym; 103 bool free_at_end = true; 104 105 while (expr->type == EXPR_ASSIGNMENT) 106 expr = strip_expr(expr->right); 107 if (expr->type != EXPR_CALL) 108 return; 109 110 arg = get_argument_from_call_expr(expr->args, param); 111 if (!arg) 112 return; 113 114 name = get_variable_from_key(arg, key, &sym); 115 if (!name || !sym) 116 goto free; 117 118 start_sm = get_sm_state(my_id, name, sym); 119 if (!start_sm && inc_dec == ATOMIC_DEC) { 120 start_sm = get_best_match(key); 121 if (start_sm) { 122 free_string(name); 123 free_at_end = false; 124 name = (char *)start_sm->name; 125 sym = start_sm->sym; 126 } 127 } 128 129 if (inc_dec == ATOMIC_INC) { 130 if (!start_sm) 131 set_start_state(name, sym, &dec); 132 // set_refcount_inc(name, sym); 133 set_state(my_id, name, sym, &inc); 134 } else { 135 // set_refcount_dec(name, sym); 136 if (!start_sm) 137 set_start_state(name, sym, &inc); 138 139 if (start_sm && start_sm->state == &inc) 140 set_state(my_id, name, sym, &start_state); 141 else 142 set_state(my_id, name, sym, &dec); 143 } 144 145 free: 146 if (free_at_end) 147 free_string(name); 148 } 149 150 static const char *primitive_funcs[] = { 151 "atomic_inc_return", 152 "atomic_add_return", 153 "atomic_sub_return", 154 "atomic_sub_and_test", 155 "atomic_dec_and_test", 156 "_atomic_dec_and_lock", 157 "atomic_dec", 158 "atomic_long_inc", 159 "atomic_long_dec", 160 "atomic_inc", 161 "atomic_sub", 162 "refcount_inc", 163 "refcount_dec", 164 "refcount_add", 165 "refcount_add_not_zero", 166 "refcount_inc_not_zero", 167 "refcount_sub_and_test", 168 "refcount_dec_and_test", 169 "atomic_dec_if_positive", 170 }; 171 172 static bool is_inc_dec_primitive(struct expression *expr) 173 { 174 int i; 175 176 while (expr->type == EXPR_ASSIGNMENT) 177 expr = strip_expr(expr->right); 178 if (expr->type != EXPR_CALL) 179 return false; 180 181 if (expr->fn->type != EXPR_SYMBOL) 182 return false; 183 184 for (i = 0; i < ARRAY_SIZE(primitive_funcs); i++) { 185 if (sym_name_is(primitive_funcs[i], expr->fn)) 186 return true; 187 } 188 189 return false; 190 } 191 192 static void db_inc(struct expression *expr, int param, char *key, char *value) 193 { 194 if (is_inc_dec_primitive(expr)) 195 return; 196 db_inc_dec(expr, param, key, ATOMIC_INC); 197 } 198 199 static void db_dec(struct expression *expr, int param, char *key, char *value) 200 { 201 if (is_inc_dec_primitive(expr)) 202 return; 203 db_inc_dec(expr, param, key, ATOMIC_DEC); 204 } 205 206 static void match_atomic_inc(const char *fn, struct expression *expr, void *_unused) 207 { 208 db_inc_dec(expr, 0, "$->counter", ATOMIC_INC); 209 } 210 211 static void match_atomic_dec(const char *fn, struct expression *expr, void *_unused) 212 { 213 db_inc_dec(expr, 0, "$->counter", ATOMIC_DEC); 214 } 215 216 static void match_atomic_add(const char *fn, struct expression *expr, void *_unused) 217 { 218 struct expression *amount; 219 sval_t sval; 220 221 amount = get_argument_from_call_expr(expr->args, 0); 222 if (get_implied_value(amount, &sval) && sval_is_negative(sval)) { 223 db_inc_dec(expr, 1, "$->counter", ATOMIC_DEC); 224 return; 225 } 226 227 db_inc_dec(expr, 1, "$->counter", ATOMIC_INC); 228 } 229 230 static void match_atomic_sub(const char *fn, struct expression *expr, void *_unused) 231 { 232 db_inc_dec(expr, 1, "$->counter", ATOMIC_DEC); 233 } 234 235 static void refcount_inc(const char *fn, struct expression *expr, void *param) 236 { 237 db_inc_dec(expr, PTR_INT(param), "$->ref.counter", ATOMIC_INC); 238 } 239 240 static void refcount_dec(const char *fn, struct expression *expr, void *param) 241 { 242 db_inc_dec(expr, PTR_INT(param), "$->ref.counter", ATOMIC_DEC); 243 } 244 245 static void pm_runtime_get_sync(const char *fn, struct expression *expr, void *param) 246 { 247 db_inc_dec(expr, PTR_INT(param), "$->power.usage_count.counter", ATOMIC_INC); 248 } 249 250 static void match_implies_inc(const char *fn, struct expression *call_expr, 251 struct expression *assign_expr, void *param) 252 { 253 db_inc_dec(call_expr, PTR_INT(param), "$->ref.counter", ATOMIC_INC); 254 } 255 256 static void match_implies_atomic_dec(const char *fn, struct expression *call_expr, 257 struct expression *assign_expr, void *param) 258 { 259 db_inc_dec(call_expr, PTR_INT(param), "$->counter", ATOMIC_DEC); 260 } 261 262 static bool is_maybe_dec(struct sm_state *sm) 263 { 264 if (sm->state == &dec) 265 return true; 266 if (slist_has_state(sm->possible, &dec) && 267 !slist_has_state(sm->possible, &inc)) 268 return true; 269 return false; 270 } 271 272 static void match_return_info(int return_id, char *return_ranges, struct expression *expr) 273 { 274 struct sm_state *sm; 275 const char *param_name; 276 int param; 277 278 if (is_impossible_path()) 279 return; 280 281 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 282 if (sm->state != &inc && !is_maybe_dec(sm)) 283 continue; 284 if (sm->state == get_state_stree(start_states, my_id, sm->name, sm->sym)) 285 continue; 286 if (parent_is_gone_var_sym(sm->name, sm->sym)) 287 continue; 288 param = get_param_num_from_sym(sm->sym); 289 if (param < 0) 290 continue; 291 param_name = get_param_name(sm); 292 if (!param_name) 293 continue; 294 sql_insert_return_states(return_id, return_ranges, 295 (sm->state == &inc) ? ATOMIC_INC : ATOMIC_DEC, 296 param, param_name, ""); 297 } END_FOR_EACH_SM(sm); 298 } 299 300 enum { 301 EMPTY, NEGATIVE, ZERO, POSITIVE, NUM_BUCKETS 302 }; 303 304 static int success_fail_positive(struct range_list *rl) 305 { 306 if (!rl) 307 return EMPTY; 308 309 if (!is_whole_rl(rl) && sval_is_negative(rl_min(rl))) 310 return NEGATIVE; 311 312 if (rl_min(rl).value == 0) 313 return ZERO; 314 315 return POSITIVE; 316 } 317 318 static void check_counter(const char *name, struct symbol *sym) 319 { 320 struct range_list *inc_lines = NULL; 321 struct range_list *dec_lines = NULL; 322 int inc_buckets[NUM_BUCKETS] = {}; 323 int dec_buckets[NUM_BUCKETS] = {}; 324 struct stree *stree, *orig_stree; 325 struct smatch_state *state; 326 struct sm_state *return_sm; 327 struct sm_state *sm; 328 sval_t line = sval_type_val(&int_ctype, 0); 329 int bucket; 330 331 /* static variable are probably just counters */ 332 if (sym->ctype.modifiers & MOD_STATIC && 333 !(sym->ctype.modifiers & MOD_TOPLEVEL)) 334 return; 335 336 FOR_EACH_PTR(get_all_return_strees(), stree) { 337 orig_stree = __swap_cur_stree(stree); 338 339 if (is_impossible_path()) 340 goto swap_stree; 341 342 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL); 343 if (!return_sm) 344 goto swap_stree; 345 line.value = return_sm->line; 346 347 if (get_state_stree(start_states, my_id, name, sym) == &inc) 348 goto swap_stree; 349 350 if (parent_is_gone_var_sym(name, sym)) 351 goto swap_stree; 352 353 sm = get_sm_state(my_id, name, sym); 354 if (sm) 355 state = sm->state; 356 else 357 state = &start_state; 358 359 if (state != &inc && 360 state != &dec && 361 state != &start_state) 362 goto swap_stree; 363 364 bucket = success_fail_positive(estate_rl(return_sm->state)); 365 366 if (state == &inc) { 367 add_range(&inc_lines, line, line); 368 inc_buckets[bucket] = true; 369 } 370 if (state == &dec || state == &start_state) { 371 add_range(&dec_lines, line, line); 372 dec_buckets[bucket] = true; 373 } 374 swap_stree: 375 __swap_cur_stree(orig_stree); 376 } END_FOR_EACH_PTR(stree); 377 378 if (inc_buckets[NEGATIVE] && 379 inc_buckets[ZERO]) { 380 // sm_warning("XXX '%s' not decremented on lines: %s.", name, show_rl(inc_lines)); 381 } 382 383 } 384 385 static void match_check_missed(struct symbol *sym) 386 { 387 struct sm_state *sm; 388 389 FOR_EACH_MY_SM(my_id, get_all_return_states(), sm) { 390 check_counter(sm->name, sm->sym); 391 } END_FOR_EACH_SM(sm); 392 } 393 394 int on_atomic_dec_path(void) 395 { 396 struct sm_state *sm; 397 398 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 399 if (sm->state == &dec) 400 return 1; 401 } END_FOR_EACH_SM(sm); 402 403 return 0; 404 } 405 406 int was_inced(const char *name, struct symbol *sym) 407 { 408 return get_state(my_id, name, sym) == &inc; 409 } 410 411 static void match_save_states(struct expression *expr) 412 { 413 push_stree(&saved_stack, start_states); 414 start_states = NULL; 415 } 416 417 static void match_restore_states(struct expression *expr) 418 { 419 start_states = pop_stree(&saved_stack); 420 } 421 422 static void match_after_func(struct symbol *sym) 423 { 424 free_stree(&start_states); 425 } 426 427 void check_atomic_inc_dec(int id) 428 { 429 my_id = id; 430 431 if (option_project != PROJ_KERNEL) 432 return; 433 434 add_unmatched_state_hook(my_id, &unmatched_state); 435 436 add_split_return_callback(match_return_info); 437 select_return_states_hook(ATOMIC_INC, &db_inc); 438 select_return_states_hook(ATOMIC_DEC, &db_dec); 439 440 add_function_hook("atomic_inc_return", &match_atomic_inc, NULL); 441 add_function_hook("atomic_add_return", &match_atomic_add, NULL); 442 add_function_hook("atomic_sub_return", &match_atomic_sub, NULL); 443 add_function_hook("atomic_sub_and_test", &match_atomic_sub, NULL); 444 add_function_hook("atomic_long_sub_and_test", &match_atomic_sub, NULL); 445 add_function_hook("atomic64_sub_and_test", &match_atomic_sub, NULL); 446 add_function_hook("atomic_dec_and_test", &match_atomic_dec, NULL); 447 add_function_hook("atomic_long_dec_and_test", &match_atomic_dec, NULL); 448 add_function_hook("atomic64_dec_and_test", &match_atomic_dec, NULL); 449 add_function_hook("_atomic_dec_and_lock", &match_atomic_dec, NULL); 450 add_function_hook("atomic_dec", &match_atomic_dec, NULL); 451 add_function_hook("atomic_dec_return", &match_atomic_dec, NULL); 452 add_function_hook("atomic_long_inc", &match_atomic_inc, NULL); 453 add_function_hook("atomic_long_dec", &match_atomic_dec, NULL); 454 add_function_hook("atomic_inc", &match_atomic_inc, NULL); 455 add_function_hook("atomic_sub", &match_atomic_sub, NULL); 456 457 add_function_hook("refcount_inc", &refcount_inc, INT_PTR(0)); 458 add_function_hook("refcount_dec", &refcount_dec, INT_PTR(0)); 459 add_function_hook("refcount_add", &refcount_inc, INT_PTR(1)); 460 461 return_implies_state("refcount_add_not_zero", 1, 1, &match_implies_inc, INT_PTR(1)); 462 return_implies_state("refcount_inc_not_zero", 1, 1, &match_implies_inc, INT_PTR(0)); 463 464 return_implies_state("atomic_dec_if_positive", 0, INT_MAX, &match_implies_atomic_dec, INT_PTR(0)); 465 466 add_function_hook("refcount_sub_and_test", &refcount_dec, INT_PTR(1)); 467 add_function_hook("refcount_dec_and_test", &refcount_dec, INT_PTR(0)); 468 469 add_function_hook("pm_runtime_get_sync", &pm_runtime_get_sync, INT_PTR(0)); 470 471 add_hook(&match_check_missed, END_FUNC_HOOK); 472 473 add_hook(&match_after_func, AFTER_FUNC_HOOK); 474 add_hook(&match_save_states, INLINE_FN_START); 475 add_hook(&match_restore_states, INLINE_FN_END); 476 }