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 struct smatch_state *unmatched_state(struct sm_state *sm)
  46 {
  47         struct smatch_state *state;
  48         sval_t sval;
  49 
  50         if (sm->state != &freed)
  51                 return &undefined;
  52 
  53         state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
  54         if (!state)
  55                 return &undefined;
  56         if (!estate_get_single_value(state, &sval) || sval.value != 0)
  57                 return &undefined;
  58         /* It makes it easier to consider NULL pointers as freed.  */
  59         return &freed;
  60 }
  61 
  62 static int is_freed(struct expression *expr)
  63 {
  64         struct sm_state *sm;
  65 
  66         sm = get_sm_state_expr(my_id, expr);
  67         if (sm && slist_has_state(sm->possible, &freed))
  68                 return 1;
  69         return 0;
  70 }
  71 
  72 static void match_symbol(struct expression *expr)
  73 {
  74         struct expression *parent;
  75         char *name;
  76 
  77         if (is_impossible_path())
  78                 return;
  79         if (__in_fake_parameter_assign)
  80                 return;
  81 
  82         parent = expr_get_parent_expr(expr);
  83         while (parent && parent->type == EXPR_PREOP && parent->op == '(')
  84                 parent = expr_get_parent_expr(parent);
  85         if (parent && parent->type == EXPR_PREOP && parent->op == '&')
  86                 return;
  87 
  88         if (!is_freed(expr))
  89                 return;
  90         name = expr_to_var(expr);
  91         sm_warning("'%s' was already freed.", name);
  92         free_string(name);
  93 }
  94 
  95 static void match_dereferences(struct expression *expr)
  96 {
  97         char *name;
  98 
  99         if (expr->type != EXPR_PREOP)
 100                 return;
 101 
 102         if (is_impossible_path())
 103                 return;
 104         if (__in_fake_parameter_assign)
 105                 return;
 106 
 107         expr = strip_expr(expr->unop);
 108         if (!is_freed(expr))
 109                 return;
 110         name = expr_to_var(expr);
 111         sm_error("dereferencing freed memory '%s'", name);
 112         set_state_expr(my_id, expr, &ok);
 113         free_string(name);
 114 }
 115 
 116 static int ignored_params[16];
 117 
 118 static void set_ignored_params(struct expression *call)
 119 {
 120         struct expression *arg;
 121         const char *p;
 122         int i;
 123 
 124         memset(&ignored_params, 0, sizeof(ignored_params));
 125 
 126         i = -1;
 127         FOR_EACH_PTR(call->args, arg) {
 128                 i++;
 129                 if (arg->type != EXPR_STRING)
 130                         continue;
 131                 goto found;
 132         } END_FOR_EACH_PTR(arg);
 133 
 134         return;
 135 
 136 found:
 137         i++;
 138         p = arg->string->data;
 139         while ((p = strchr(p, '%'))) {
 140                 if (i >= ARRAY_SIZE(ignored_params))
 141                         return;
 142                 p++;
 143                 if (*p == '%') {
 144                         p++;
 145                         continue;
 146                 }
 147                 if (*p == '.')
 148                         p++;
 149                 if (*p == '*')
 150                         i++;
 151                 if (*p == 'p')
 152                         ignored_params[i] = 1;
 153                 i++;
 154         }
 155 }
 156 
 157 static int is_free_func(struct expression *fn)
 158 {
 159         char *name;
 160         int ret = 0;
 161 
 162         name = expr_to_str(fn);
 163         if (!name)
 164                 return 0;
 165         if (strstr(name, "free"))
 166                 ret = 1;
 167         free_string(name);
 168 
 169         return ret;
 170 }
 171 
 172 static void match_call(struct expression *expr)
 173 {
 174         struct expression *arg;
 175         char *name;
 176         int i;
 177 
 178         if (is_impossible_path())
 179                 return;
 180 
 181         set_ignored_params(expr);
 182 
 183         i = -1;
 184         FOR_EACH_PTR(expr->args, arg) {
 185                 i++;
 186                 if (!is_pointer(arg))
 187                         continue;
 188                 if (!is_freed(arg))
 189                         continue;
 190                 if (ignored_params[i])
 191                         continue;
 192 
 193                 name = expr_to_var(arg);
 194                 if (is_free_func(expr->fn))
 195                         sm_error("double free of '%s'", name);
 196                 else
 197                         sm_warning("passing freed memory '%s'", name);
 198                 set_state_expr(my_id, arg, &ok);
 199                 free_string(name);
 200         } END_FOR_EACH_PTR(arg);
 201 }
 202 
 203 static void match_return(struct expression *expr)
 204 {
 205         char *name;
 206 
 207         if (is_impossible_path())
 208                 return;
 209 
 210         if (!expr)
 211                 return;
 212         if (!is_freed(expr))
 213                 return;
 214 
 215         name = expr_to_var(expr);
 216         sm_warning("returning freed memory '%s'", name);
 217         set_state_expr(my_id, expr, &ok);
 218         free_string(name);
 219 }
 220 
 221 static void match_free(const char *fn, struct expression *expr, void *param)
 222 {
 223         struct expression *arg;
 224 
 225         if (is_impossible_path())
 226                 return;
 227 
 228         arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
 229         if (!arg)
 230                 return;
 231         if (is_freed(arg)) {
 232                 char *name = expr_to_var(arg);
 233 
 234                 sm_error("double free of '%s'", name);
 235                 free_string(name);
 236         }
 237         set_state_expr(my_id, arg, &freed);
 238 }
 239 
 240 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
 241 {
 242         struct expression *arg;
 243         char *name;
 244         struct symbol *sym;
 245         struct sm_state *sm;
 246 
 247         while (expr->type == EXPR_ASSIGNMENT)
 248                 expr = strip_expr(expr->right);
 249         if (expr->type != EXPR_CALL)
 250                 return;
 251 
 252         arg = get_argument_from_call_expr(expr->args, param);
 253         if (!arg)
 254                 return;
 255         name = get_variable_from_key(arg, key, &sym);
 256         if (!name || !sym)
 257                 goto free;
 258 
 259         if (!is_impossible_path()) {
 260                 sm = get_sm_state(my_id, name, sym);
 261                 if (sm && slist_has_state(sm->possible, &freed)) {
 262                         sm_warning("'%s' double freed", name);
 263                         set_state(my_id, name, sym, &ok);  /* fixme: doesn't silence anything.  I know */
 264                 }
 265         }
 266 
 267         set_state(my_id, name, sym, &freed);
 268 free:
 269         free_string(name);
 270 }
 271 
 272 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
 273 {
 274         char buf[256];
 275         char *start;
 276         char *end;
 277         struct smatch_state *state;
 278 
 279         strncpy(buf, name, sizeof(buf) - 1);
 280         buf[sizeof(buf) - 1] = '\0';
 281 
 282         start = &buf[0];
 283         while ((*start == '&'))
 284                 start++;
 285 
 286         while ((end = strrchr(start, '-'))) {
 287                 *end = '\0';
 288                 state = __get_state(my_id, start, sym);
 289                 if (state == &freed)
 290                         return 1;
 291         }
 292         return 0;
 293 }
 294 
 295 int parent_is_free_strict(struct expression *expr)
 296 {
 297         struct symbol *sym;
 298         char *var;
 299         int ret = 0;
 300 
 301         expr = strip_expr(expr);
 302         var = expr_to_var_sym(expr, &sym);
 303         if (!var || !sym)
 304                 goto free;
 305         ret = parent_is_free_var_sym_strict(var, sym);
 306 free:
 307         free_string(var);
 308         return ret;
 309 }
 310 
 311 static void match_untracked(struct expression *call, int param)
 312 {
 313         struct state_list *slist = NULL;
 314         struct expression *arg;
 315         struct sm_state *sm;
 316         char *name;
 317         char buf[64];
 318         int len;
 319 
 320         arg = get_argument_from_call_expr(call->args, param);
 321         if (!arg)
 322                 return;
 323 
 324         name = expr_to_var(arg);
 325         if (!name)
 326                 return;
 327         snprintf(buf, sizeof(buf), "%s->", name);
 328         free_string(name);
 329         len = strlen(buf);
 330 
 331         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 332                 if (strncmp(sm->name, buf, len) == 0)
 333                         add_ptr_list(&slist, sm);
 334         } END_FOR_EACH_SM(sm);
 335 
 336         FOR_EACH_PTR(slist, sm) {
 337                 set_state(sm->owner, sm->name, sm->sym, &ok);
 338         } END_FOR_EACH_PTR(sm);
 339 
 340         free_slist(&slist);
 341 }
 342 
 343 void check_free_strict(int id)
 344 {
 345         my_id = id;
 346 
 347         if (option_project != PROJ_KERNEL)
 348                 return;
 349 
 350         add_function_hook("kfree", &match_free, INT_PTR(0));
 351         add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
 352 
 353         if (option_spammy)
 354                 add_hook(&match_symbol, SYM_HOOK);
 355         add_hook(&match_dereferences, DEREF_HOOK);
 356         add_hook(&match_call, FUNCTION_CALL_HOOK);
 357         add_hook(&match_return, RETURN_HOOK);
 358 
 359         add_modification_hook_late(my_id, &ok_to_use);
 360         add_pre_merge_hook(my_id, &pre_merge_hook);
 361         add_unmatched_state_hook(my_id, &unmatched_state);
 362 
 363         select_return_states_hook(PARAM_FREED, &set_param_freed);
 364         add_untracked_param_hook(&match_untracked);
 365 }