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 (is_zero(expr->left)) 51 return strip_condition(expr->right); 52 if (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 (__prev_stmt) { 135 prev_pos = __prev_stmt->pos; 136 prev_pos.line -= 3; 137 } else { 138 prev_pos = __cur_stmt->pos; 139 prev_pos.line -= 5; 140 } 141 142 FOR_EACH_PTR_REVERSE(big_statement_stack, stmt) { 143 if (stmt->pos.line < prev_pos.line) 144 return 0; 145 pos = stmt->pos; 146 ident = get_macro_name(pos); 147 if (name_means_synchronize(ident)) 148 return 1; 149 ident = pos_ident(pos); 150 if (!ident) 151 continue; 152 if (strcmp(ident, "if") == 0) { 153 pos.pos += 4; 154 ident = pos_ident(pos); 155 if (!ident) 156 continue; 157 } 158 if (name_means_synchronize(ident)) 159 return 1; 160 } END_FOR_EACH_PTR_REVERSE(stmt); 161 return 0; 162 } 163 164 static void match_condition(struct expression *expr) 165 { 166 struct smatch_state *state; 167 sval_t dummy; 168 char *name; 169 170 if (inside_loop()) 171 return; 172 173 if (get_value(expr, &dummy)) 174 return; 175 176 if (get_macro_name(expr->pos)) 177 return; 178 179 state = get_stored_condition(expr); 180 if (!state || !state->data) 181 return; 182 if (get_macro_name(((struct expression *)state->data)->pos)) 183 return; 184 185 /* 186 * we allow double checking for NULL because people do this all the time 187 * and trying to stop them is a losers' battle. 188 */ 189 if (is_pointer(expr) && implied_condition_true(expr)) 190 return; 191 192 if (definitely_inside_loop()) { 193 struct symbol *sym; 194 195 if (__inline_fn) 196 return; 197 198 name = expr_to_var_sym(expr, &sym); 199 if (!name) 200 return; 201 set_state_expr(my_id, expr, &checked); 202 set_state_stree(&to_check, my_id, name, sym, &checked); 203 free_string(name); 204 return; 205 } 206 207 if (is_obvious_else(state->data)) 208 return; 209 210 /* 211 * It's common to test something, then take a lock and test if it is 212 * still true. 213 */ 214 if (previous_statement_was_synchronize()) 215 return; 216 217 name = expr_to_str(expr); 218 sm_warning("we tested '%s' before and it was '%s'", name, state->name); 219 free_string(name); 220 } 221 222 int get_check_line(struct sm_state *sm) 223 { 224 struct sm_state *tmp; 225 226 FOR_EACH_PTR(sm->possible, tmp) { 227 if (tmp->state == &checked) 228 return tmp->line; 229 } END_FOR_EACH_PTR(tmp); 230 231 return get_lineno(); 232 } 233 234 static void after_loop(struct statement *stmt) 235 { 236 struct sm_state *check, *sm; 237 238 if (!stmt || stmt->type != STMT_ITERATOR) 239 return; 240 if (definitely_inside_loop()) 241 return; 242 if (__inline_fn) 243 return; 244 245 FOR_EACH_SM(to_check, check) { 246 continue; 247 sm = get_sm_state(my_id, check->name, check->sym); 248 continue; 249 if (!sm) 250 continue; 251 if (slist_has_state(sm->possible, &modified)) 252 continue; 253 254 sm_printf("%s:%d %s() ", get_filename(), get_check_line(sm), get_function()); 255 sm_printf("warn: we tested '%s' already\n", check->name); 256 } END_FOR_EACH_SM(check); 257 258 free_stree(&to_check); 259 } 260 261 static void match_func_end(struct symbol *sym) 262 { 263 if (__inline_fn) 264 return; 265 if (to_check) 266 sm_msg("debug: odd... found an function without an end."); 267 free_stree(&to_check); 268 } 269 270 void check_double_checking(int id) 271 { 272 my_id = id; 273 274 if (!option_spammy) 275 return; 276 277 add_hook(&match_condition, CONDITION_HOOK); 278 add_modification_hook(my_id, &set_modified); 279 add_hook(after_loop, STMT_HOOK_AFTER); 280 add_hook(&match_func_end, AFTER_FUNC_HOOK); 281 }