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 }