1 /* 2 * Copyright (C) 2014 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 #define _GNU_SOURCE 19 #include <string.h> 20 #include "smatch.h" 21 #include "smatch_slist.h" 22 23 static int my_id; 24 25 STATE(checked); 26 STATE(modified); 27 28 struct stree *to_check; 29 30 static struct statement *get_cur_stmt(void) 31 { 32 return last_ptr_list((struct ptr_list *)big_statement_stack); 33 } 34 35 static void set_modified(struct sm_state *sm, struct expression *mod_expr) 36 { 37 set_state(my_id, sm->name, sm->sym, &modified); 38 } 39 40 static struct expression *strip_condition(struct expression *expr) 41 { 42 expr = strip_expr(expr); 43 44 if (expr->type == EXPR_PREOP && expr->op == '!') 45 return strip_condition(expr->unop); 46 47 if (expr->type == EXPR_COMPARE && 48 (expr->op == SPECIAL_EQUAL || 49 expr->op == SPECIAL_NOTEQUAL)) { 50 if (expr_is_zero(expr->left)) 51 return strip_condition(expr->right); 52 if (expr_is_zero(expr->right)) 53 return strip_condition(expr->left); 54 } 55 56 return expr; 57 } 58 59 static int conditions_match(struct expression *cond, struct expression *prev) 60 { 61 prev = strip_condition(prev); 62 63 if (prev == cond) 64 return 1; 65 66 if (prev->type == EXPR_LOGICAL) { 67 if (conditions_match(cond, prev->left) || 68 conditions_match(cond, prev->right)) 69 return 1; 70 } 71 72 return 0; 73 } 74 75 /* 76 * People like to do "if (foo) { ... } else if (!foo) { ... }". Don't 77 * complain when they do that even though it is nonsense. 78 */ 79 static int is_obvious_else(struct expression *cond) 80 { 81 struct statement *parent; 82 struct expression *prev; 83 84 if (!get_cur_stmt()) 85 return 0; 86 parent = get_cur_stmt()->parent; 87 if (!parent) 88 return 0; 89 90 if (parent->type != STMT_IF) 91 return 0; 92 93 if (!parent->if_false) 94 return 0; 95 if (parent->if_false != get_cur_stmt()) 96 return 0; 97 98 prev = strip_condition(parent->if_conditional); 99 100 return conditions_match(cond, prev); 101 } 102 103 static int name_means_synchronize(const char *name) 104 { 105 if (!name) 106 return 0; 107 108 if (strcasestr(name, "wait")) 109 return 1; 110 if (strcasestr(name, "down")) 111 return 1; 112 if (strcasestr(name, "lock") && !strcasestr(name, "unlock")) 113 return 1; 114 if (strcasestr(name, "delay")) 115 return 1; 116 if (strcasestr(name, "schedule")) 117 return 1; 118 if (strcmp(name, "smp_rmb") == 0) 119 return 1; 120 if (strcmp(name, "mb") == 0) 121 return 1; 122 if (strcmp(name, "barrier") == 0) 123 return 1; 124 return 0; 125 } 126 127 static int previous_statement_was_synchronize(void) 128 { 129 struct statement *stmt; 130 struct position pos; 131 struct position prev_pos; 132 char *ident; 133 134 if (!__cur_stmt) 135 return 0; 136 137 if (__prev_stmt) { 138 prev_pos = __prev_stmt->pos; 139 prev_pos.line -= 3; 140 } else { 141 prev_pos = __cur_stmt->pos; 142 prev_pos.line -= 5; 143 } 144 145 FOR_EACH_PTR_REVERSE(big_statement_stack, stmt) { 146 if (stmt->pos.line < prev_pos.line) 147 return 0; 148 pos = stmt->pos; 149 ident = get_macro_name(pos); 150 if (name_means_synchronize(ident)) 151 return 1; 152 ident = pos_ident(pos); 153 if (!ident) 154 continue; 155 if (strcmp(ident, "if") == 0) { 156 pos.pos += 4; 157 ident = pos_ident(pos); 158 if (!ident) 159 continue; 160 } 161 if (name_means_synchronize(ident)) 162 return 1; 163 } END_FOR_EACH_PTR_REVERSE(stmt); 164 return 0; 165 } 166 167 static void match_condition(struct expression *expr) 168 { 169 struct smatch_state *state; 170 sval_t dummy; 171 char *name; 172 173 if (inside_loop()) 174 return; 175 176 if (get_value(expr, &dummy)) 177 return; 178 179 if (get_macro_name(expr->pos)) 180 return; 181 182 state = get_stored_condition(expr); 183 if (!state || !state->data) 184 return; 185 if (get_macro_name(((struct expression *)state->data)->pos)) 186 return; 187 188 /* 189 * we allow double checking for NULL because people do this all the time 190 * and trying to stop them is a losers' battle. 191 */ 192 if (is_pointer(expr) && implied_condition_true(expr)) 193 return; 194 195 if (definitely_inside_loop()) { 196 struct symbol *sym; 197 198 if (__inline_fn) 199 return; 200 201 name = expr_to_var_sym(expr, &sym); 202 if (!name) 203 return; 204 set_state_expr(my_id, expr, &checked); 205 set_state_stree(&to_check, my_id, name, sym, &checked); 206 free_string(name); 207 return; 208 } 209 210 if (is_obvious_else(state->data)) 211 return; 212 213 /* 214 * It's common to test something, then take a lock and test if it is 215 * still true. 216 */ 217 if (previous_statement_was_synchronize()) 218 return; 219 220 name = expr_to_str(expr); 221 sm_warning("we tested '%s' before and it was '%s'", name, state->name); 222 free_string(name); 223 } 224 225 int get_check_line(struct sm_state *sm) 226 { 227 struct sm_state *tmp; 228 229 FOR_EACH_PTR(sm->possible, tmp) { 230 if (tmp->state == &checked) 231 return tmp->line; 232 } END_FOR_EACH_PTR(tmp); 233 234 return get_lineno(); 235 } 236 237 static void after_loop(struct statement *stmt) 238 { 239 struct sm_state *check, *sm; 240 241 if (!stmt || stmt->type != STMT_ITERATOR) 242 return; 243 if (definitely_inside_loop()) 244 return; 245 if (__inline_fn) 246 return; 247 248 FOR_EACH_SM(to_check, check) { 249 continue; 250 sm = get_sm_state(my_id, check->name, check->sym); 251 continue; 252 if (!sm) 253 continue; 254 if (slist_has_state(sm->possible, &modified)) 255 continue; 256 257 sm_printf("%s:%d %s() ", get_filename(), get_check_line(sm), get_function()); 258 sm_printf("warn: we tested '%s' already\n", check->name); 259 } END_FOR_EACH_SM(check); 260 261 free_stree(&to_check); 262 } 263 264 static void match_func_end(struct symbol *sym) 265 { 266 if (__inline_fn) 267 return; 268 if (to_check) 269 sm_msg("debug: odd... found an function without an end."); 270 free_stree(&to_check); 271 } 272 273 void check_double_checking(int id) 274 { 275 my_id = id; 276 277 if (!option_spammy) 278 return; 279 280 add_hook(&match_condition, CONDITION_HOOK); 281 add_modification_hook(my_id, &set_modified); 282 add_hook(after_loop, STMT_HOOK_AFTER); 283 add_hook(&match_func_end, AFTER_FUNC_HOOK); 284 } 285