1 /*
   2  * Copyright (C) 2008 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 #include <fcntl.h>
  19 #include <unistd.h>
  20 #include "parse.h"
  21 #include "smatch.h"
  22 #include "smatch_slist.h"
  23 
  24 static void check_sm_is_leaked(struct sm_state *sm);
  25 
  26 static int my_id;
  27 
  28 STATE(allocated);
  29 STATE(assigned);
  30 STATE(isfree);
  31 STATE(malloced);
  32 STATE(isnull);
  33 STATE(unfree);
  34 
  35 /*
  36   malloced --> allocated --> assigned --> isfree +
  37             \-> isnull.    \-> isfree +
  38 
  39   isfree --> unfree.
  40           \-> isnull.
  41 */
  42 
  43 static struct tracker_list *arguments;
  44 
  45 static const char *allocation_funcs[] = {
  46         "malloc",
  47         "kmalloc",
  48         "kzalloc",
  49         NULL,
  50 };
  51 
  52 static char *get_parent_name(struct symbol *sym)
  53 {
  54         static char buf[256];
  55 
  56         if (!sym || !sym->ident)
  57                 return NULL;
  58 
  59         snprintf(buf, 255, "-%s", sym->ident->name);
  60         buf[255] = '\0';
  61         return alloc_string(buf);
  62 }
  63 
  64 static int is_parent_sym(const char *name)
  65 {
  66         if (!strncmp(name, "-", 1))
  67                 return 1;
  68         return 0;
  69 }
  70 
  71 static int is_complex(struct expression *expr)
  72 {
  73         char *name;
  74         int ret = 1;
  75 
  76         name = expr_to_var(expr);
  77         if (name)
  78                 ret = 0;
  79         free_string(name);
  80         return ret;
  81 }
  82 
  83 static struct smatch_state *unmatched_state(struct sm_state *sm)
  84 {
  85         if (is_parent_sym(sm->name))
  86                 return &assigned;
  87         return &undefined;
  88 }
  89 
  90 static void assign_parent(struct symbol *sym)
  91 {
  92         char *name;
  93 
  94         name = get_parent_name(sym);
  95         if (!name)
  96                 return;
  97         set_state(my_id, name, sym, &assigned);
  98         free_string(name);
  99 }
 100 
 101 static int parent_is_assigned(struct symbol *sym)
 102 {
 103         struct smatch_state *state;
 104         char *name;
 105 
 106         name = get_parent_name(sym);
 107         if (!name)
 108                 return 0;
 109         state = get_state(my_id, name, sym);
 110         free_string(name);
 111         if (state == &assigned)
 112                 return 1;
 113         return 0;
 114 }
 115 
 116 static int is_allocation(struct expression *expr)
 117 {
 118         char *fn_name;
 119         int i;
 120 
 121         if (expr->type != EXPR_CALL)
 122                 return 0;
 123 
 124         if (!(fn_name = expr_to_var_sym(expr->fn, NULL)))
 125                 return 0;
 126 
 127         for (i = 0; allocation_funcs[i]; i++) {
 128                 if (!strcmp(fn_name, allocation_funcs[i])) {
 129                         free_string(fn_name);
 130                         return 1;
 131                 }
 132         }
 133         free_string(fn_name);
 134         return 0;
 135 }
 136 
 137 static int is_freed(const char *name, struct symbol *sym)
 138 {
 139         struct state_list *slist;
 140 
 141         slist = get_possible_states(my_id, name, sym);
 142         if (slist_has_state(slist, &isfree)) {
 143                 return 1;
 144         }
 145         return 0;
 146 }
 147 
 148 static int is_argument(struct symbol *sym)
 149 {
 150         struct tracker *arg;
 151 
 152         FOR_EACH_PTR(arguments, arg) {
 153                 if (arg->sym == sym)
 154                         return 1;
 155         } END_FOR_EACH_PTR(arg);
 156         return 0;
 157 }
 158 
 159 static void match_function_def(struct symbol *sym)
 160 {
 161         struct symbol *arg;
 162 
 163         FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
 164                 add_tracker(&arguments, my_id, (arg->ident?arg->ident->name:"NULL"), arg);
 165         } END_FOR_EACH_PTR(arg);
 166 }
 167 
 168 static int is_parent(struct expression *expr)
 169 {
 170         if (expr->type == EXPR_DEREF)
 171                 return 0;
 172         return 1;
 173 }
 174 
 175 static void match_assign(struct expression *expr)
 176 {
 177         struct expression *left, *right;
 178         char *left_name = NULL;
 179         char *right_name = NULL;
 180         struct symbol *left_sym, *right_sym;
 181         struct smatch_state *state;
 182         struct state_list *slist;
 183         struct sm_state *tmp;
 184 
 185         left = strip_expr(expr->left);
 186         left_name = expr_to_str_sym(left, &left_sym);
 187 
 188         right = strip_expr(expr->right);
 189         while (right->type == EXPR_ASSIGNMENT)
 190                 right = right->left;
 191 
 192         if (left_name && left_sym && is_allocation(right) && 
 193             !(left_sym->ctype.modifiers & 
 194               (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
 195             !parent_is_assigned(left_sym)) {
 196                 set_state(my_id, left_name, left_sym, &malloced);
 197                 goto exit;
 198         }
 199 
 200         right_name = expr_to_str_sym(right, &right_sym);
 201 
 202         if (right_name && (state = get_state(my_id, right_name, right_sym))) {
 203                 if (state == &isfree && !is_complex(right))
 204                         sm_error("assigning freed pointer '%s'", right_name);
 205                 set_state(my_id, right_name, right_sym, &assigned);
 206         }
 207 
 208         if (is_zero(expr->right)) {
 209                 slist = get_possible_states(my_id, left_name, left_sym);
 210 
 211                 FOR_EACH_PTR(slist, tmp) {
 212                         check_sm_is_leaked(tmp);
 213                 } END_FOR_EACH_PTR(tmp);
 214         }
 215 
 216         if (is_freed(left_name, left_sym)) {
 217                 set_state(my_id, left_name, left_sym, &unfree);
 218         }
 219         if (left_name && is_parent(left))
 220                 assign_parent(left_sym);
 221         if (right_name && is_parent(right))
 222                 assign_parent(right_sym);
 223 exit:
 224         free_string(left_name);
 225         free_string(right_name);
 226 }
 227 
 228 static int is_null(const char *name, struct symbol *sym)
 229 {
 230         struct smatch_state *state;
 231 
 232         state = get_state(my_id, name, sym);
 233         if (state && !strcmp(state->name, "isnull"))
 234                 return 1;
 235         return 0;
 236 }
 237 
 238 static void set_unfree(struct sm_state *sm, struct expression *mod_expr)
 239 {
 240         if (slist_has_state(sm->possible, &isfree))
 241                 set_state(my_id, sm->name, sm->sym, &unfree);
 242 }
 243 
 244 static void match_free_func(const char *fn, struct expression *expr, void *data)
 245 {
 246         struct expression *ptr_expr;
 247         char *ptr_name;
 248         struct symbol *ptr_sym;
 249         int arg_num = PTR_INT(data);
 250 
 251         ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
 252         ptr_name = expr_to_var_sym(ptr_expr, &ptr_sym);
 253         if (!ptr_name)
 254                 return;
 255         set_state(my_id, ptr_name, ptr_sym, &isfree);
 256         free_string(ptr_name);
 257 }
 258 
 259 static int possibly_allocated(struct state_list *slist)
 260 {
 261         struct sm_state *tmp;
 262 
 263         FOR_EACH_PTR(slist, tmp) {
 264                 if (tmp->state == &allocated)
 265                         return 1;
 266                 if (tmp->state == &malloced)
 267                         return 1;
 268         } END_FOR_EACH_PTR(tmp);
 269         return 0;
 270 }
 271 
 272 static void check_sm_is_leaked(struct sm_state *sm)
 273 {
 274         if (possibly_allocated(sm->possible) && 
 275                 !is_null(sm->name, sm->sym) &&
 276                 !is_argument(sm->sym) && 
 277                 !parent_is_assigned(sm->sym))
 278                 sm_error("memory leak of '%s'", sm->name);
 279 }
 280 
 281 static void check_tracker_is_leaked(struct tracker *t)
 282 {
 283         struct sm_state *sm;
 284 
 285         sm = get_sm_state(t->owner, t->name, t->sym);
 286         if (sm)
 287                 check_sm_is_leaked(sm);
 288         __free_tracker(t);
 289 }
 290 
 291 static void match_declarations(struct symbol *sym)
 292 {
 293         const char *name;
 294 
 295         if ((get_base_type(sym))->type == SYM_ARRAY) {
 296                 return;
 297         }
 298 
 299         name = sym->ident->name;
 300 
 301         if (sym->initializer) {
 302                 if (is_allocation(sym->initializer)) {
 303                         set_state(my_id, name, sym, &malloced);
 304                         add_scope_hook((scope_hook *)&check_tracker_is_leaked,
 305                                 alloc_tracker(my_id, name, sym));
 306                         scoped_state(my_id, name, sym);
 307                 } else {
 308                         assign_parent(sym);
 309                 }
 310         }
 311 }
 312 
 313 static void check_for_allocated(void)
 314 {
 315         struct stree *stree;
 316         struct sm_state *tmp;
 317 
 318         stree = __get_cur_stree();
 319         FOR_EACH_MY_SM(my_id, stree, tmp) {
 320                 check_sm_is_leaked(tmp);
 321         } END_FOR_EACH_SM(tmp);
 322 }
 323 
 324 static void match_return(struct expression *ret_value)
 325 {
 326         char *name;
 327         struct symbol *sym;
 328 
 329         if (__inline_fn)
 330                 return;
 331         name = expr_to_str_sym(ret_value, &sym);
 332         if (sym)
 333                 assign_parent(sym);
 334         free_string(name);
 335         check_for_allocated();
 336 }
 337 
 338 static void set_new_true_false_paths(const char *name, struct symbol *sym)
 339 {
 340         struct smatch_state *tmp;
 341 
 342         tmp = get_state(my_id, name, sym);
 343 
 344         if (!tmp) {
 345                 return;
 346         }
 347 
 348         if (tmp == &isfree) {
 349                 sm_warning("why do you care about freed memory? '%s'", name);
 350         }
 351 
 352         if (tmp == &assigned) {
 353                 /* we don't care about assigned pointers any more */
 354                 return;
 355         }
 356         set_true_false_states(my_id, name, sym, &allocated, &isnull);
 357 }
 358 
 359 static void match_condition(struct expression *expr)
 360 {
 361         struct symbol *sym;
 362         char *name;
 363 
 364         expr = strip_expr(expr);
 365         switch (expr->type) {
 366         case EXPR_PREOP:
 367         case EXPR_SYMBOL:
 368         case EXPR_DEREF:
 369                 name = expr_to_var_sym(expr, &sym);
 370                 if (!name)
 371                         return;
 372                 set_new_true_false_paths(name, sym);
 373                 free_string(name);
 374                 return;
 375         case EXPR_ASSIGNMENT:
 376                  /* You have to deal with stuff like if (a = b = c) */
 377                 match_condition(expr->right);
 378                 match_condition(expr->left);
 379                 return;
 380         default:
 381                 return;
 382         }
 383 }
 384 
 385 static void match_function_call(struct expression *expr)
 386 {
 387         struct expression *tmp;
 388         struct symbol *sym;
 389         char *name;
 390         struct sm_state *state;
 391 
 392         FOR_EACH_PTR(expr->args, tmp) {
 393                 tmp = strip_expr(tmp);
 394                 name = expr_to_str_sym(tmp, &sym);
 395                 if (!name)
 396                         continue;
 397                 if ((state = get_sm_state(my_id, name, sym))) {
 398                         if (possibly_allocated(state->possible)) {
 399                                 set_state(my_id, name, sym, &assigned);
 400                         }
 401                 }
 402                 assign_parent(sym);
 403                 free_string(name);
 404         } END_FOR_EACH_PTR(tmp);
 405 }
 406 
 407 static void match_end_func(struct symbol *sym)
 408 {
 409         if (__inline_fn)
 410                 return;
 411         check_for_allocated();
 412 }
 413 
 414 static void match_after_func(struct symbol *sym)
 415 {
 416         if (__inline_fn)
 417                 return;
 418         free_trackers_and_list(&arguments);
 419 }
 420 
 421 static void register_funcs_from_file(void)
 422 {
 423         struct token *token;
 424         const char *func;
 425         int arg;
 426 
 427         token = get_tokens_file("kernel.frees_argument");
 428         if (!token)
 429                 return;
 430         if (token_type(token) != TOKEN_STREAMBEGIN)
 431                 return;
 432         token = token->next;
 433         while (token_type(token) != TOKEN_STREAMEND) {
 434                 if (token_type(token) != TOKEN_IDENT)
 435                         return;
 436                 func = show_ident(token->ident);
 437                 token = token->next;
 438                 if (token_type(token) != TOKEN_NUMBER)
 439                         return;
 440                 arg = atoi(token->number);
 441                 add_function_hook(func, &match_free_func, INT_PTR(arg));
 442                 token = token->next;
 443         }
 444         clear_token_alloc();
 445 }
 446 
 447 void check_memory(int id)
 448 {
 449         my_id = id;
 450         add_unmatched_state_hook(my_id, &unmatched_state);
 451         add_hook(&match_function_def, FUNC_DEF_HOOK);
 452         add_hook(&match_declarations, DECLARATION_HOOK);
 453         add_hook(&match_function_call, FUNCTION_CALL_HOOK);
 454         add_hook(&match_condition, CONDITION_HOOK);
 455         add_hook(&match_assign, ASSIGNMENT_HOOK);
 456         add_hook(&match_return, RETURN_HOOK);
 457         add_hook(&match_end_func, END_FUNC_HOOK);
 458         add_hook(&match_after_func, AFTER_FUNC_HOOK);
 459         add_modification_hook(my_id, &set_unfree);
 460         if (option_project == PROJ_KERNEL) {
 461                 add_function_hook("kfree", &match_free_func, (void *)0);
 462                 register_funcs_from_file();
 463         } else {
 464                 add_function_hook("free", &match_free_func, (void *)0);
 465         }
 466 }