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 char *alloc_parent_str(struct symbol *sym)
  50 {
  51         static char buf[256];
  52 
  53         if (!sym || !sym->ident)
  54                 return NULL;
  55 
  56         snprintf(buf, 255, "%s", sym->ident->name);
  57         buf[255] = '\0';
  58         return alloc_string(buf);
  59 }
  60 
  61 static char *get_parent_from_expr(struct expression *expr, struct symbol **sym)
  62 {
  63         char *name;
  64 
  65         expr = strip_expr(expr);
  66 
  67         name = expr_to_str_sym(expr, sym);
  68         free_string(name);
  69         if (!name || !*sym || !(*sym)->ident) {
  70                 *sym = NULL;
  71                 return NULL;
  72         }
  73         return alloc_parent_str(*sym);
  74 }
  75 
  76 static int is_local(struct expression *expr)
  77 {
  78         char *name;
  79         struct symbol *sym;
  80         int ret = 0;
  81 
  82         name = expr_to_str_sym(expr, &sym);
  83         if (!name || !sym)
  84                 goto out;
  85         if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
  86                 goto out;
  87         ret = 1;
  88 out:
  89         free_string(name);
  90         return ret;
  91 }
  92 
  93 static int is_param(struct expression *expr)
  94 {
  95         char *name;
  96         struct symbol *sym;
  97         struct symbol *tmp;
  98         int ret = 0;
  99 
 100         name = expr_to_str_sym(expr, &sym);
 101         if (!name || !sym)
 102                 goto out;
 103         FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
 104                 if (tmp == sym) {
 105                         ret = 1;
 106                         goto out;
 107                 }
 108         } END_FOR_EACH_PTR(tmp);
 109 out:
 110         free_string(name);
 111         return ret;
 112         
 113 }
 114 
 115 static void match_alloc(const char *fn, struct expression *expr, void *unused)
 116 {
 117         if (!is_local(expr->left))
 118                 return;
 119         if (is_param(expr->left))
 120                 return;
 121         if (expr->left->type != EXPR_SYMBOL)
 122                 return;
 123         set_state_expr(my_id, expr->left, &allocated);
 124 }
 125 
 126 static void match_condition(struct expression *expr)
 127 {
 128         struct sm_state *sm;
 129 
 130         expr = strip_expr(expr);
 131 
 132         switch (expr->type) {
 133         case EXPR_PREOP:
 134         case EXPR_SYMBOL:
 135         case EXPR_DEREF:
 136                 sm = get_sm_state_expr(my_id, expr);
 137                 if (sm && slist_has_state(sm->possible, &allocated))
 138                         set_true_false_states_expr(my_id, expr, NULL, &ok);
 139                 return;
 140         case EXPR_ASSIGNMENT:
 141                  /* You have to deal with stuff like if (a = b = c) */
 142                 match_condition(expr->left);
 143                 return;
 144         default:
 145                 return;
 146         }
 147 }
 148 
 149 static void set_parent(struct expression *expr, struct smatch_state *state)
 150 {
 151         char *name;
 152         struct symbol *sym;
 153 
 154         expr = strip_expr(expr);
 155         if (!expr)
 156                 return;
 157         if (expr->type == EXPR_CONDITIONAL ||
 158             expr->type == EXPR_SELECT) {
 159                 set_parent(expr->cond_true, state);
 160                 set_parent(expr->cond_false, state);
 161                 return;
 162         }
 163 
 164         name = get_parent_from_expr(expr, &sym);
 165         if (!name || !sym)
 166                 goto free;
 167         if (state == &ok && !get_state(my_id, name, sym))
 168                 goto free;
 169         set_state(my_id, name, sym, state);
 170 free:
 171         free_string(name);
 172 }
 173 
 174 static void match_function_call(struct expression *expr)
 175 {
 176         struct expression *tmp;
 177 
 178         FOR_EACH_PTR(expr->args, tmp) {
 179                 set_parent(tmp, &ok);
 180         } END_FOR_EACH_PTR(tmp);
 181 }
 182 
 183 static void warn_if_allocated(struct expression *expr)
 184 {
 185         struct sm_state *sm;
 186         char *name;
 187         sval_t sval;
 188 
 189         if (get_implied_value(expr, &sval) && sval.value == 0)
 190                 return;
 191 
 192         sm = get_sm_state_expr(my_id, expr);
 193         if (!sm)
 194                 return;
 195         if (!slist_has_state(sm->possible, &allocated))
 196                 return;
 197 
 198         name = expr_to_var(expr);
 199         sm_warning("overwrite may leak '%s'", name);
 200         free_string(name);
 201 
 202         /* silence further warnings */
 203         set_state_expr(my_id, expr, &ok);
 204 }
 205 
 206 static void match_assign(struct expression *expr)
 207 {
 208         struct expression *right;
 209 
 210         right = expr->right;
 211 
 212         while (right->type == EXPR_ASSIGNMENT)
 213                 right = right->left;
 214 
 215         warn_if_allocated(expr->left);
 216         set_parent(right, &ok);
 217 }
 218 
 219 static void check_for_allocated(void)
 220 {
 221         struct stree *stree;
 222         struct sm_state *tmp;
 223 
 224         stree = __get_cur_stree();
 225         FOR_EACH_MY_SM(my_id, stree, tmp) {
 226                 if (!slist_has_state(tmp->possible, &allocated))
 227                         continue;
 228                 sm_warning("possible memory leak of '%s'", tmp->name);
 229         } END_FOR_EACH_SM(tmp);
 230 }
 231 
 232 static void match_return(struct expression *ret_value)
 233 {
 234         if (__inline_fn)
 235                 return;
 236         set_parent(ret_value, &ok);
 237         check_for_allocated();
 238 }
 239 
 240 static void match_end_func(struct symbol *sym)
 241 {
 242         if (__inline_fn)
 243                 return;
 244         check_for_allocated();
 245 }
 246 
 247 void check_leaks(int id)
 248 {
 249         my_id = id;
 250 
 251         switch (option_project) {
 252         case PROJ_KERNEL:
 253                 add_function_assign_hook("kmalloc", &match_alloc, NULL);
 254                 add_function_assign_hook("kzalloc", &match_alloc, NULL);
 255                 add_function_assign_hook("kmemdup", &match_alloc, NULL);
 256                 break;
 257         case PROJ_ILLUMOS_USER:
 258                 add_function_assign_hook("libld_malloc", &match_alloc, NULL);
 259                 add_function_assign_hook("libld_calloc", &match_alloc, NULL);
 260                 /* FALLTHROUGH */
 261         default:
 262                 add_function_assign_hook("malloc", &match_alloc, NULL);
 263                 add_function_assign_hook("calloc", &match_alloc, NULL);
 264         }
 265         
 266         add_hook(&match_condition, CONDITION_HOOK);
 267 
 268         add_hook(&match_function_call, FUNCTION_CALL_HOOK);
 269         add_hook(&match_assign, ASSIGNMENT_HOOK);
 270 
 271         add_hook(&match_return, RETURN_HOOK);
 272         add_hook(&match_end_func, END_FUNC_HOOK);
 273 }