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  * The point of this check is to look for leaks.
  20  * foo = malloc();  // <- mark it as allocated.
  21  * A variable becomes &ok if we:
  22  * 1) assign it to another variable.
  23  * 2) pass it to a function.
  24  *
  25  * One complication is dealing with stuff like:
  26  * foo->bar = malloc();
  27  * foo->baz = malloc();
  28  * foo = something();
  29  *
  30  * The work around is that for now what this check only
  31  * checks simple expressions and doesn't check whether
  32  * foo->bar is leaked.
  33  * 
  34  */
  35 
  36 #include <fcntl.h>
  37 #include <unistd.h>
  38 #include "parse.h"
  39 #include "smatch.h"
  40 #include "smatch_slist.h"
  41 
  42 static int my_id;
  43 
  44 STATE(allocated);
  45 STATE(ok);
  46 
  47 static void set_parent(struct expression *expr, struct smatch_state *state);
  48 
  49 static const char *allocation_funcs[] = {
  50         "malloc",
  51         "kmalloc",
  52         "kzalloc",
  53         "kmemdup",
  54 };
  55 
  56 static char *alloc_parent_str(struct symbol *sym)
  57 {
  58         static char buf[256];
  59 
  60         if (!sym || !sym->ident)
  61                 return NULL;
  62 
  63         snprintf(buf, 255, "%s", sym->ident->name);
  64         buf[255] = '\0';
  65         return alloc_string(buf);
  66 }
  67 
  68 static char *get_parent_from_expr(struct expression *expr, struct symbol **sym)
  69 {
  70         char *name;
  71 
  72         expr = strip_expr(expr);
  73 
  74         name = expr_to_str_sym(expr, sym);
  75         free_string(name);
  76         if (!name || !*sym || !(*sym)->ident) {
  77                 *sym = NULL;
  78                 return NULL;
  79         }
  80         return alloc_parent_str(*sym);
  81 }
  82 
  83 static int is_local(struct expression *expr)
  84 {
  85         char *name;
  86         struct symbol *sym;
  87         int ret = 0;
  88 
  89         name = expr_to_str_sym(expr, &sym);
  90         if (!name || !sym)
  91                 goto out;
  92         if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
  93                 goto out;
  94         ret = 1;
  95 out:
  96         free_string(name);
  97         return ret;
  98 }
  99 
 100 static int is_param(struct expression *expr)
 101 {
 102         char *name;
 103         struct symbol *sym;
 104         struct symbol *tmp;
 105         int ret = 0;
 106 
 107         name = expr_to_str_sym(expr, &sym);
 108         if (!name || !sym)
 109                 goto out;
 110         FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
 111                 if (tmp == sym) {
 112                         ret = 1;
 113                         goto out;
 114                 }
 115         } END_FOR_EACH_PTR(tmp);
 116 out:
 117         free_string(name);
 118         return ret;
 119         
 120 }
 121 
 122 static void match_alloc(const char *fn, struct expression *expr, void *unused)
 123 {
 124         if (!is_local(expr->left))
 125                 return;
 126         if (is_param(expr->left))
 127                 return;
 128         if (expr->left->type != EXPR_SYMBOL)
 129                 return;
 130         set_state_expr(my_id, expr->left, &allocated);
 131 }
 132 
 133 static void match_condition(struct expression *expr)
 134 {
 135         struct sm_state *sm;
 136 
 137         expr = strip_expr(expr);
 138 
 139         switch (expr->type) {
 140         case EXPR_PREOP:
 141         case EXPR_SYMBOL:
 142         case EXPR_DEREF:
 143                 sm = get_sm_state_expr(my_id, expr);
 144                 if (sm && slist_has_state(sm->possible, &allocated))
 145                         set_true_false_states_expr(my_id, expr, NULL, &ok);
 146                 return;
 147         case EXPR_ASSIGNMENT:
 148                  /* You have to deal with stuff like if (a = b = c) */
 149                 match_condition(expr->left);
 150                 return;
 151         default:
 152                 return;
 153         }
 154 }
 155 
 156 static void set_parent(struct expression *expr, struct smatch_state *state)
 157 {
 158         char *name;
 159         struct symbol *sym;
 160 
 161         expr = strip_expr(expr);
 162         if (!expr)
 163                 return;
 164         if (expr->type == EXPR_CONDITIONAL ||
 165             expr->type == EXPR_SELECT) {
 166                 set_parent(expr->cond_true, state);
 167                 set_parent(expr->cond_false, state);
 168                 return;
 169         }
 170 
 171         name = get_parent_from_expr(expr, &sym);
 172         if (!name || !sym)
 173                 goto free;
 174         if (state == &ok && !get_state(my_id, name, sym))
 175                 goto free;
 176         set_state(my_id, name, sym, state);
 177 free:
 178         free_string(name);
 179 }
 180 
 181 static void match_function_call(struct expression *expr)
 182 {
 183         struct expression *tmp;
 184 
 185         FOR_EACH_PTR(expr->args, tmp) {
 186                 set_parent(tmp, &ok);
 187         } END_FOR_EACH_PTR(tmp);
 188 }
 189 
 190 static void warn_if_allocated(struct expression *expr)
 191 {
 192         struct sm_state *sm;
 193         char *name;
 194         sval_t sval;
 195 
 196         if (get_implied_value(expr, &sval) && sval.value == 0)
 197                 return;
 198 
 199         sm = get_sm_state_expr(my_id, expr);
 200         if (!sm)
 201                 return;
 202         if (!slist_has_state(sm->possible, &allocated))
 203                 return;
 204 
 205         name = expr_to_var(expr);
 206         sm_warning("overwrite may leak '%s'", name);
 207         free_string(name);
 208 
 209         /* silence further warnings */
 210         set_state_expr(my_id, expr, &ok);
 211 }
 212 
 213 static void match_assign(struct expression *expr)
 214 {
 215         struct expression *right;
 216 
 217         right = expr->right;
 218 
 219         while (right->type == EXPR_ASSIGNMENT)
 220                 right = right->left;
 221 
 222         warn_if_allocated(expr->left);
 223         set_parent(right, &ok);
 224 }
 225 
 226 static void check_for_allocated(void)
 227 {
 228         struct stree *stree;
 229         struct sm_state *tmp;
 230 
 231         stree = __get_cur_stree();
 232         FOR_EACH_MY_SM(my_id, stree, tmp) {
 233                 if (!slist_has_state(tmp->possible, &allocated))
 234                         continue;
 235                 sm_warning("possible memory leak of '%s'", tmp->name);
 236         } END_FOR_EACH_SM(tmp);
 237 }
 238 
 239 static void match_return(struct expression *ret_value)
 240 {
 241         if (__inline_fn)
 242                 return;
 243         set_parent(ret_value, &ok);
 244         check_for_allocated();
 245 }
 246 
 247 static void match_end_func(struct symbol *sym)
 248 {
 249         if (__inline_fn)
 250                 return;
 251         check_for_allocated();
 252 }
 253 
 254 void check_leaks(int id)
 255 {
 256         int i;
 257 
 258         my_id = id;
 259 
 260         for (i = 0; i < ARRAY_SIZE(allocation_funcs); i++)
 261                 add_function_assign_hook(allocation_funcs[i], &match_alloc, NULL);
 262 
 263         add_hook(&match_condition, CONDITION_HOOK);
 264 
 265         add_hook(&match_function_call, FUNCTION_CALL_HOOK);
 266         add_hook(&match_assign, ASSIGNMENT_HOOK);
 267 
 268         add_hook(&match_return, RETURN_HOOK);
 269         add_hook(&match_end_func, END_FUNC_HOOK);
 270 }