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  * check_memory() is getting too big and messy.
  20  *
  21  */
  22 
  23 #include <string.h>
  24 #include "smatch.h"
  25 #include "smatch_slist.h"
  26 #include "smatch_extra.h"
  27 
  28 static int my_id;
  29 
  30 STATE(freed);
  31 STATE(ok);
  32 
  33 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
  34 {
  35         if (sm->state != &ok)
  36                 set_state(my_id, sm->name, sm->sym, &ok);
  37 }
  38 
  39 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
  40 {
  41         if (is_impossible_path())
  42                 set_state(my_id, cur->name, cur->sym, &ok);
  43 }
  44 
  45 static int is_freed(struct expression *expr)
  46 {
  47         struct sm_state *sm;
  48 
  49         sm = get_sm_state_expr(my_id, expr);
  50         if (sm && slist_has_state(sm->possible, &freed))
  51                 return 1;
  52         return 0;
  53 }
  54 
  55 static void match_symbol(struct expression *expr)
  56 {
  57         struct expression *parent;
  58         char *name;
  59 
  60         if (is_impossible_path())
  61                 return;
  62         if (__in_fake_parameter_assign)
  63                 return;
  64 
  65         parent = expr_get_parent_expr(expr);
  66         while (parent && parent->type == EXPR_PREOP && parent->op == '(')
  67                 parent = expr_get_parent_expr(parent);
  68         if (parent && parent->type == EXPR_PREOP && parent->op == '&')
  69                 return;
  70 
  71         if (!is_freed(expr))
  72                 return;
  73         name = expr_to_var(expr);
  74         sm_warning("'%s' was already freed.", name);
  75         free_string(name);
  76 }
  77 
  78 static void match_dereferences(struct expression *expr)
  79 {
  80         char *name;
  81 
  82         if (__in_fake_parameter_assign)
  83                 return;
  84 
  85         if (expr->type != EXPR_PREOP)
  86                 return;
  87 
  88         if (is_impossible_path())
  89                 return;
  90 
  91         expr = strip_expr(expr->unop);
  92         if (!is_freed(expr))
  93                 return;
  94         name = expr_to_var(expr);
  95         sm_error("dereferencing freed memory '%s'", name);
  96         set_state_expr(my_id, expr, &ok);
  97         free_string(name);
  98 }
  99 
 100 static int ignored_params[16];
 101 
 102 static void set_ignored_params(struct expression *call)
 103 {
 104         struct expression *arg;
 105         const char *p;
 106         int i;
 107 
 108         memset(&ignored_params, 0, sizeof(ignored_params));
 109 
 110         i = -1;
 111         FOR_EACH_PTR(call->args, arg) {
 112                 i++;
 113                 if (arg->type != EXPR_STRING)
 114                         continue;
 115                 goto found;
 116         } END_FOR_EACH_PTR(arg);
 117 
 118         return;
 119 
 120 found:
 121         i++;
 122         p = arg->string->data;
 123         while ((p = strchr(p, '%'))) {
 124                 if (i >= ARRAY_SIZE(ignored_params))
 125                         return;
 126                 p++;
 127                 if (*p == '%') {
 128                         p++;
 129                         continue;
 130                 }
 131                 if (*p == '.')
 132                         p++;
 133                 if (*p == '*')
 134                         i++;
 135                 if (*p == 'p')
 136                         ignored_params[i] = 1;
 137                 i++;
 138         }
 139 }
 140 
 141 static int is_free_func(struct expression *fn)
 142 {
 143         char *name;
 144         int ret = 0;
 145 
 146         name = expr_to_str(fn);
 147         if (!name)
 148                 return 0;
 149         if (strstr(name, "free"))
 150                 ret = 1;
 151         free_string(name);
 152 
 153         return ret;
 154 }
 155 
 156 static void match_call(struct expression *expr)
 157 {
 158         struct expression *arg;
 159         char *name;
 160         int i;
 161 
 162         if (is_impossible_path())
 163                 return;
 164 
 165         set_ignored_params(expr);
 166 
 167         i = -1;
 168         FOR_EACH_PTR(expr->args, arg) {
 169                 i++;
 170                 if (!is_pointer(arg))
 171                         continue;
 172                 if (!is_freed(arg))
 173                         continue;
 174                 if (ignored_params[i])
 175                         continue;
 176 
 177                 name = expr_to_var(arg);
 178                 if (is_free_func(expr->fn))
 179                         sm_error("double free of '%s'", name);
 180                 else
 181                         sm_warning("passing freed memory '%s'", name);
 182                 set_state_expr(my_id, arg, &ok);
 183                 free_string(name);
 184         } END_FOR_EACH_PTR(arg);
 185 }
 186 
 187 static void match_return(struct expression *expr)
 188 {
 189         char *name;
 190 
 191         if (is_impossible_path())
 192                 return;
 193         if (__in_fake_parameter_assign)
 194                 return;
 195 
 196         if (!expr)
 197                 return;
 198         if (!is_freed(expr))
 199                 return;
 200 
 201         name = expr_to_var(expr);
 202         sm_warning("returning freed memory '%s'", name);
 203         set_state_expr(my_id, expr, &ok);
 204         free_string(name);
 205 }
 206 
 207 static void match_free(const char *fn, struct expression *expr, void *param)
 208 {
 209         struct expression *arg;
 210 
 211         if (is_impossible_path())
 212                 return;
 213 
 214         arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
 215         if (!arg)
 216                 return;
 217         if (is_freed(arg)) {
 218                 char *name = expr_to_var(arg);
 219 
 220                 sm_error("double free of '%s'", name);
 221                 free_string(name);
 222         }
 223         set_state_expr(my_id, arg, &freed);
 224 }
 225 
 226 static void set_param_freed(struct expression *call, struct expression *arg, char *key, char *unused)
 227 {
 228         struct symbol *sym;
 229         char *name;
 230 
 231         name = get_variable_from_key(arg, key, &sym);
 232         if (!name || !sym)
 233                 goto free;
 234 
 235         set_state(my_id, name, sym, &freed);
 236 free:
 237         free_string(name);
 238 }
 239 
 240 int parent_is_free_var_sym(const char *name, struct symbol *sym)
 241 {
 242         char buf[256];
 243         char *start;
 244         char *end;
 245         struct smatch_state *state;
 246 
 247         if (option_project == PROJ_KERNEL)
 248                 return parent_is_free_var_sym_strict(name, sym);
 249 
 250         strncpy(buf, name, sizeof(buf) - 1);
 251         buf[sizeof(buf) - 1] = '\0';
 252 
 253         start = &buf[0];
 254         while ((*start == '&'))
 255                 start++;
 256 
 257         while ((end = strrchr(start, '-'))) {
 258                 *end = '\0';
 259                 state = __get_state(my_id, start, sym);
 260                 if (state == &freed)
 261                         return 1;
 262         }
 263         return 0;
 264 }
 265 
 266 int parent_is_free(struct expression *expr)
 267 {
 268         struct symbol *sym;
 269         char *var;
 270         int ret = 0;
 271 
 272         expr = strip_expr(expr);
 273         var = expr_to_var_sym(expr, &sym);
 274         if (!var || !sym)
 275                 goto free;
 276         ret = parent_is_free_var_sym(var, sym);
 277 free:
 278         free_string(var);
 279         return ret;
 280 }
 281 
 282 void check_free(int id)
 283 {
 284         my_id = id;
 285 
 286         if (option_project == PROJ_KERNEL) {
 287                 /* The kernel use check_free_strict.c */
 288                 return;
 289         }
 290 
 291         add_function_hook("free", &match_free, INT_PTR(0));
 292 
 293         if (option_spammy)
 294                 add_hook(&match_symbol, SYM_HOOK);
 295         add_hook(&match_dereferences, DEREF_HOOK);
 296         add_hook(&match_call, FUNCTION_CALL_HOOK);
 297         add_hook(&match_return, RETURN_HOOK);
 298 
 299         add_modification_hook(my_id, &ok_to_use);
 300         select_return_implies_hook(PARAM_FREED, &set_param_freed);
 301         add_pre_merge_hook(my_id, &pre_merge_hook);
 302 }