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 *sm)
  40 {
  41         if (is_impossible_path())
  42                 set_state(my_id, sm->name, sm->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 (expr->type != EXPR_PREOP)
  83                 return;
  84 
  85         if (is_impossible_path())
  86                 return;
  87         if (__in_fake_parameter_assign)
  88                 return;
  89 
  90         expr = strip_expr(expr->unop);
  91         if (!is_freed(expr))
  92                 return;
  93         name = expr_to_var(expr);
  94         sm_error("dereferencing freed memory '%s'", name);
  95         set_state_expr(my_id, expr, &ok);
  96         free_string(name);
  97 }
  98 
  99 static int ignored_params[16];
 100 
 101 static void set_ignored_params(struct expression *call)
 102 {
 103         struct expression *arg;
 104         const char *p;
 105         int i;
 106 
 107         memset(&ignored_params, 0, sizeof(ignored_params));
 108 
 109         i = -1;
 110         FOR_EACH_PTR(call->args, arg) {
 111                 i++;
 112                 if (arg->type != EXPR_STRING)
 113                         continue;
 114                 goto found;
 115         } END_FOR_EACH_PTR(arg);
 116 
 117         return;
 118 
 119 found:
 120         i++;
 121         p = arg->string->data;
 122         while ((p = strchr(p, '%'))) {
 123                 if (i >= ARRAY_SIZE(ignored_params))
 124                         return;
 125                 p++;
 126                 if (*p == '%') {
 127                         p++;
 128                         continue;
 129                 }
 130                 if (*p == '.')
 131                         p++;
 132                 if (*p == '*')
 133                         i++;
 134                 if (*p == 'p')
 135                         ignored_params[i] = 1;
 136                 i++;
 137         }
 138 }
 139 
 140 static int is_free_func(struct expression *fn)
 141 {
 142         char *name;
 143         int ret = 0;
 144 
 145         name = expr_to_str(fn);
 146         if (!name)
 147                 return 0;
 148         if (strstr(name, "free"))
 149                 ret = 1;
 150         free_string(name);
 151 
 152         return ret;
 153 }
 154 
 155 static void match_call(struct expression *expr)
 156 {
 157         struct expression *arg;
 158         char *name;
 159         int i;
 160 
 161         if (is_impossible_path())
 162                 return;
 163 
 164         set_ignored_params(expr);
 165 
 166         i = -1;
 167         FOR_EACH_PTR(expr->args, arg) {
 168                 i++;
 169                 if (!is_pointer(arg))
 170                         continue;
 171                 if (!is_freed(arg))
 172                         continue;
 173                 if (ignored_params[i])
 174                         continue;
 175 
 176                 name = expr_to_var(arg);
 177                 if (is_free_func(expr->fn))
 178                         sm_error("double free of '%s'", name);
 179                 else
 180                         sm_warning("passing freed memory '%s'", name);
 181                 set_state_expr(my_id, arg, &ok);
 182                 free_string(name);
 183         } END_FOR_EACH_PTR(arg);
 184 }
 185 
 186 static void match_return(struct expression *expr)
 187 {
 188         char *name;
 189 
 190         if (is_impossible_path())
 191                 return;
 192 
 193         if (!expr)
 194                 return;
 195         if (!is_freed(expr))
 196                 return;
 197 
 198         name = expr_to_var(expr);
 199         sm_warning("returning freed memory '%s'", name);
 200         set_state_expr(my_id, expr, &ok);
 201         free_string(name);
 202 }
 203 
 204 static void match_free(const char *fn, struct expression *expr, void *param)
 205 {
 206         struct expression *arg;
 207 
 208         if (is_impossible_path())
 209                 return;
 210 
 211         arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
 212         if (!arg)
 213                 return;
 214         if (is_freed(arg)) {
 215                 char *name = expr_to_var(arg);
 216 
 217                 sm_error("double free of '%s'", name);
 218                 free_string(name);
 219         }
 220         set_state_expr(my_id, arg, &freed);
 221 }
 222 
 223 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
 224 {
 225         struct expression *arg;
 226         char *name;
 227         struct symbol *sym;
 228         struct sm_state *sm;
 229 
 230         while (expr->type == EXPR_ASSIGNMENT)
 231                 expr = strip_expr(expr->right);
 232         if (expr->type != EXPR_CALL)
 233                 return;
 234 
 235         arg = get_argument_from_call_expr(expr->args, param);
 236         if (!arg)
 237                 return;
 238         name = get_variable_from_key(arg, key, &sym);
 239         if (!name || !sym)
 240                 goto free;
 241 
 242         if (!is_impossible_path()) {
 243                 sm = get_sm_state(my_id, name, sym);
 244                 if (sm && slist_has_state(sm->possible, &freed)) {
 245                         sm_warning("'%s' double freed", name);
 246                         set_state(my_id, name, sym, &ok);  /* fixme: doesn't silence anything.  I know */
 247                 }
 248         }
 249 
 250         set_state(my_id, name, sym, &freed);
 251 free:
 252         free_string(name);
 253 }
 254 
 255 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
 256 {
 257         char buf[256];
 258         char *start;
 259         char *end;
 260         struct smatch_state *state;
 261 
 262         strncpy(buf, name, sizeof(buf) - 1);
 263         buf[sizeof(buf) - 1] = '\0';
 264 
 265         start = &buf[0];
 266         while ((*start == '&'))
 267                 start++;
 268 
 269         while ((end = strrchr(start, '-'))) {
 270                 *end = '\0';
 271                 state = __get_state(my_id, start, sym);
 272                 if (state == &freed)
 273                         return 1;
 274         }
 275         return 0;
 276 }
 277 
 278 int parent_is_free_strict(struct expression *expr)
 279 {
 280         struct symbol *sym;
 281         char *var;
 282         int ret = 0;
 283 
 284         expr = strip_expr(expr);
 285         var = expr_to_var_sym(expr, &sym);
 286         if (!var || !sym)
 287                 goto free;
 288         ret = parent_is_free_var_sym_strict(var, sym);
 289 free:
 290         free_string(var);
 291         return ret;
 292 }
 293 
 294 void check_free_strict(int id)
 295 {
 296         my_id = id;
 297 
 298         if (option_project != PROJ_KERNEL)
 299                 return;
 300 
 301         add_function_hook("kfree", &match_free, INT_PTR(0));
 302         add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
 303 
 304         if (option_spammy)
 305                 add_hook(&match_symbol, SYM_HOOK);
 306         add_hook(&match_dereferences, DEREF_HOOK);
 307         add_hook(&match_call, FUNCTION_CALL_HOOK);
 308         add_hook(&match_return, RETURN_HOOK);
 309 
 310         add_modification_hook_late(my_id, &ok_to_use);
 311         add_pre_merge_hook(my_id, &pre_merge_hook);
 312 
 313         select_return_states_hook(PARAM_FREED, &set_param_freed);
 314 }