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 }