1 /*
   2  * Copyright (C) 2010 Dan Carpenter.
   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 /*
  19  * This is like check_deref_check.c except that it complains about code like:
  20  * if (a)
  21  *        a->foo = 42;
  22  * a->bar = 7;
  23  *
  24  * Of course, Smatch has complained about these for forever but the problem is
  25  * the old scripts were too messy and complicated and generated too many false
  26  * positives.
  27  *
  28  * This check is supposed to be simpler because it only looks for one kind of 
  29  * null dereference bug instead of every kind.  It also gets rid of the false 
  30  * positives caused by the checks that happen inside macros.
  31  *
  32  */
  33 
  34 #include "smatch.h"
  35 #include "smatch_slist.h"
  36 #include "smatch_extra.h"
  37 
  38 static int my_id;
  39 
  40 STATE(null);
  41 STATE(ok);
  42 
  43 static void is_ok(struct sm_state *sm, struct expression *mod_expr)
  44 {
  45         set_state(my_id, sm->name, sm->sym, &ok);
  46 }
  47 
  48 static void check_dereference(struct expression *expr)
  49 {
  50         struct sm_state *sm;
  51         struct sm_state *tmp;
  52 
  53         if (__in_fake_assign)
  54                 return;
  55 
  56         expr = strip_expr(expr);
  57 
  58         sm = get_sm_state_expr(my_id, expr);
  59         if (!sm)
  60                 return;
  61         if (is_ignored(my_id, sm->name, sm->sym))
  62                 return;
  63         if (implied_not_equal(expr, 0))
  64                 return;
  65 
  66         FOR_EACH_PTR(sm->possible, tmp) {
  67                 if (tmp->state == &merged)
  68                         continue;
  69                 if (tmp->state == &ok)
  70                         continue;
  71                 if (tmp->state == &null) {
  72                         sm_error("we previously assumed '%s' could be null (see line %d)",
  73                                tmp->name, tmp->line);
  74                         add_ignore(my_id, sm->name, sm->sym);
  75                         return;
  76                 }
  77         } END_FOR_EACH_PTR(tmp);
  78 }
  79 
  80 static void check_dereference_name_sym(char *name, struct symbol *sym)
  81 {
  82         struct sm_state *sm;
  83         struct sm_state *tmp;
  84 
  85         sm = get_sm_state(my_id, name, sym);
  86         if (!sm)
  87                 return;
  88         if (is_ignored(my_id, sm->name, sm->sym))
  89                 return;
  90         if (implied_not_equal_name_sym(name, sym, 0))
  91                 return;
  92 
  93         FOR_EACH_PTR(sm->possible, tmp) {
  94                 if (tmp->state == &merged)
  95                         continue;
  96                 if (tmp->state == &ok)
  97                         continue;
  98                 if (tmp->state == &null) {
  99                         sm_error("we previously assumed '%s' could be null (see line %d)",
 100                                tmp->name, tmp->line);
 101                         add_ignore(my_id, sm->name, sm->sym);
 102                         return;
 103                 }
 104         } END_FOR_EACH_PTR(tmp);
 105 }
 106 
 107 static void match_dereferences(struct expression *expr)
 108 {
 109         if (expr->type != EXPR_PREOP)
 110                 return;
 111         check_dereference(expr->unop);
 112 }
 113 
 114 static void match_pointer_as_array(struct expression *expr)
 115 {
 116         if (!is_array(expr))
 117                 return;
 118         check_dereference(get_array_base(expr));
 119 }
 120 
 121 static void set_param_dereferenced(struct expression *call, struct expression *arg, char *key, char *unused)
 122 {
 123         struct symbol *sym;
 124         char *name;
 125 
 126         name = get_variable_from_key(arg, key, &sym);
 127         if (!name || !sym)
 128                 goto free;
 129 
 130         check_dereference_name_sym(name, sym);
 131 free:
 132         free_string(name);
 133 }
 134 
 135 static void match_condition(struct expression *expr)
 136 {
 137         struct smatch_state *true_state = NULL;
 138         char *name;
 139 
 140         name = get_macro_name(expr->pos);
 141         if (name &&
 142             (strcmp(name, "likely") != 0 && strcmp(name, "unlikely") != 0))
 143                 return;
 144 
 145         if (!is_pointer(expr))
 146                 return;
 147 
 148         if (expr->type == EXPR_ASSIGNMENT) {
 149                 match_condition(expr->right);
 150                 match_condition(expr->left);
 151         }
 152 
 153         if (implied_not_equal(expr, 0))
 154                 return;
 155 
 156         if (get_state_expr(my_id, expr))
 157                 true_state = &ok;
 158 
 159         set_true_false_states_expr(my_id, expr, true_state, &null);
 160 }
 161 
 162 void check_check_deref(int id)
 163 {
 164         my_id = id;
 165 
 166         add_modification_hook(my_id, &is_ok);
 167         add_hook(&match_dereferences, DEREF_HOOK);
 168         add_hook(&match_pointer_as_array, OP_HOOK);
 169         select_return_implies_hook(DEREFERENCE, &set_param_dereferenced);
 170         add_hook(&match_condition, CONDITION_HOOK);
 171 }