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