11f5207b7SJohn Levon /* 21f5207b7SJohn Levon * Copyright (C) 2010 Dan Carpenter. 31f5207b7SJohn Levon * 41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or 51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License 61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2 71f5207b7SJohn Levon * of the License, or (at your option) any later version. 81f5207b7SJohn Levon * 91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful, 101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of 111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121f5207b7SJohn Levon * GNU General Public License for more details. 131f5207b7SJohn Levon * 141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License 151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 161f5207b7SJohn Levon */ 171f5207b7SJohn Levon 181f5207b7SJohn Levon /* 191f5207b7SJohn Levon * check_memory() is getting too big and messy. 201f5207b7SJohn Levon * 211f5207b7SJohn Levon */ 221f5207b7SJohn Levon 231f5207b7SJohn Levon #include <string.h> 241f5207b7SJohn Levon #include "smatch.h" 251f5207b7SJohn Levon #include "smatch_slist.h" 261f5207b7SJohn Levon #include "smatch_extra.h" 271f5207b7SJohn Levon 281f5207b7SJohn Levon static int my_id; 291f5207b7SJohn Levon 301f5207b7SJohn Levon STATE(freed); 311f5207b7SJohn Levon STATE(ok); 321f5207b7SJohn Levon 331f5207b7SJohn Levon static void ok_to_use(struct sm_state *sm, struct expression *mod_expr) 341f5207b7SJohn Levon { 351f5207b7SJohn Levon if (sm->state != &ok) 361f5207b7SJohn Levon set_state(my_id, sm->name, sm->sym, &ok); 371f5207b7SJohn Levon } 381f5207b7SJohn Levon 391f5207b7SJohn Levon static void pre_merge_hook(struct sm_state *sm) 401f5207b7SJohn Levon { 411f5207b7SJohn Levon if (is_impossible_path()) 421f5207b7SJohn Levon set_state(my_id, sm->name, sm->sym, &ok); 431f5207b7SJohn Levon } 441f5207b7SJohn Levon 451f5207b7SJohn Levon static int is_freed(struct expression *expr) 461f5207b7SJohn Levon { 471f5207b7SJohn Levon struct sm_state *sm; 481f5207b7SJohn Levon 491f5207b7SJohn Levon sm = get_sm_state_expr(my_id, expr); 501f5207b7SJohn Levon if (sm && slist_has_state(sm->possible, &freed)) 511f5207b7SJohn Levon return 1; 521f5207b7SJohn Levon return 0; 531f5207b7SJohn Levon } 541f5207b7SJohn Levon 551f5207b7SJohn Levon static void match_symbol(struct expression *expr) 561f5207b7SJohn Levon { 571f5207b7SJohn Levon struct expression *parent; 581f5207b7SJohn Levon char *name; 591f5207b7SJohn Levon 601f5207b7SJohn Levon if (is_impossible_path()) 611f5207b7SJohn Levon return; 621f5207b7SJohn Levon if (__in_fake_parameter_assign) 631f5207b7SJohn Levon return; 641f5207b7SJohn Levon 651f5207b7SJohn Levon parent = expr_get_parent_expr(expr); 661f5207b7SJohn Levon while (parent && parent->type == EXPR_PREOP && parent->op == '(') 671f5207b7SJohn Levon parent = expr_get_parent_expr(parent); 681f5207b7SJohn Levon if (parent && parent->type == EXPR_PREOP && parent->op == '&') 691f5207b7SJohn Levon return; 701f5207b7SJohn Levon 711f5207b7SJohn Levon if (!is_freed(expr)) 721f5207b7SJohn Levon return; 731f5207b7SJohn Levon name = expr_to_var(expr); 741f5207b7SJohn Levon sm_warning("'%s' was already freed.", name); 751f5207b7SJohn Levon free_string(name); 761f5207b7SJohn Levon } 771f5207b7SJohn Levon 781f5207b7SJohn Levon static void match_dereferences(struct expression *expr) 791f5207b7SJohn Levon { 801f5207b7SJohn Levon char *name; 811f5207b7SJohn Levon 821f5207b7SJohn Levon if (expr->type != EXPR_PREOP) 831f5207b7SJohn Levon return; 841f5207b7SJohn Levon 851f5207b7SJohn Levon if (is_impossible_path()) 861f5207b7SJohn Levon return; 871f5207b7SJohn Levon if (__in_fake_parameter_assign) 881f5207b7SJohn Levon return; 891f5207b7SJohn Levon 901f5207b7SJohn Levon expr = strip_expr(expr->unop); 911f5207b7SJohn Levon if (!is_freed(expr)) 921f5207b7SJohn Levon return; 931f5207b7SJohn Levon name = expr_to_var(expr); 941f5207b7SJohn Levon sm_error("dereferencing freed memory '%s'", name); 951f5207b7SJohn Levon set_state_expr(my_id, expr, &ok); 961f5207b7SJohn Levon free_string(name); 971f5207b7SJohn Levon } 981f5207b7SJohn Levon 991f5207b7SJohn Levon static int ignored_params[16]; 1001f5207b7SJohn Levon 1011f5207b7SJohn Levon static void set_ignored_params(struct expression *call) 1021f5207b7SJohn Levon { 1031f5207b7SJohn Levon struct expression *arg; 1041f5207b7SJohn Levon const char *p; 1051f5207b7SJohn Levon int i; 1061f5207b7SJohn Levon 1071f5207b7SJohn Levon memset(&ignored_params, 0, sizeof(ignored_params)); 1081f5207b7SJohn Levon 1091f5207b7SJohn Levon i = -1; 1101f5207b7SJohn Levon FOR_EACH_PTR(call->args, arg) { 1111f5207b7SJohn Levon i++; 1121f5207b7SJohn Levon if (arg->type != EXPR_STRING) 1131f5207b7SJohn Levon continue; 1141f5207b7SJohn Levon goto found; 1151f5207b7SJohn Levon } END_FOR_EACH_PTR(arg); 1161f5207b7SJohn Levon 1171f5207b7SJohn Levon return; 1181f5207b7SJohn Levon 1191f5207b7SJohn Levon found: 1201f5207b7SJohn Levon i++; 1211f5207b7SJohn Levon p = arg->string->data; 1221f5207b7SJohn Levon while ((p = strchr(p, '%'))) { 1231f5207b7SJohn Levon if (i >= ARRAY_SIZE(ignored_params)) 1241f5207b7SJohn Levon return; 1251f5207b7SJohn Levon p++; 1261f5207b7SJohn Levon if (*p == '%') { 1271f5207b7SJohn Levon p++; 1281f5207b7SJohn Levon continue; 1291f5207b7SJohn Levon } 1301f5207b7SJohn Levon if (*p == '.') 1311f5207b7SJohn Levon p++; 1321f5207b7SJohn Levon if (*p == '*') 1331f5207b7SJohn Levon i++; 1341f5207b7SJohn Levon if (*p == 'p') 1351f5207b7SJohn Levon ignored_params[i] = 1; 1361f5207b7SJohn Levon i++; 1371f5207b7SJohn Levon } 1381f5207b7SJohn Levon } 1391f5207b7SJohn Levon 1401f5207b7SJohn Levon static int is_free_func(struct expression *fn) 1411f5207b7SJohn Levon { 1421f5207b7SJohn Levon char *name; 1431f5207b7SJohn Levon int ret = 0; 1441f5207b7SJohn Levon 1451f5207b7SJohn Levon name = expr_to_str(fn); 1461f5207b7SJohn Levon if (!name) 1471f5207b7SJohn Levon return 0; 1481f5207b7SJohn Levon if (strstr(name, "free")) 1491f5207b7SJohn Levon ret = 1; 1501f5207b7SJohn Levon free_string(name); 1511f5207b7SJohn Levon 1521f5207b7SJohn Levon return ret; 1531f5207b7SJohn Levon } 1541f5207b7SJohn Levon 1551f5207b7SJohn Levon static void match_call(struct expression *expr) 1561f5207b7SJohn Levon { 1571f5207b7SJohn Levon struct expression *arg; 1581f5207b7SJohn Levon char *name; 1591f5207b7SJohn Levon int i; 1601f5207b7SJohn Levon 1611f5207b7SJohn Levon if (is_impossible_path()) 1621f5207b7SJohn Levon return; 1631f5207b7SJohn Levon 1641f5207b7SJohn Levon set_ignored_params(expr); 1651f5207b7SJohn Levon 1661f5207b7SJohn Levon i = -1; 1671f5207b7SJohn Levon FOR_EACH_PTR(expr->args, arg) { 1681f5207b7SJohn Levon i++; 1691f5207b7SJohn Levon if (!is_pointer(arg)) 1701f5207b7SJohn Levon continue; 1711f5207b7SJohn Levon if (!is_freed(arg)) 1721f5207b7SJohn Levon continue; 1731f5207b7SJohn Levon if (ignored_params[i]) 1741f5207b7SJohn Levon continue; 1751f5207b7SJohn Levon 1761f5207b7SJohn Levon name = expr_to_var(arg); 1771f5207b7SJohn Levon if (is_free_func(expr->fn)) 1781f5207b7SJohn Levon sm_error("double free of '%s'", name); 1791f5207b7SJohn Levon else 1801f5207b7SJohn Levon sm_warning("passing freed memory '%s'", name); 1811f5207b7SJohn Levon set_state_expr(my_id, arg, &ok); 1821f5207b7SJohn Levon free_string(name); 1831f5207b7SJohn Levon } END_FOR_EACH_PTR(arg); 1841f5207b7SJohn Levon } 1851f5207b7SJohn Levon 1861f5207b7SJohn Levon static void match_return(struct expression *expr) 1871f5207b7SJohn Levon { 1881f5207b7SJohn Levon char *name; 1891f5207b7SJohn Levon 1901f5207b7SJohn Levon if (is_impossible_path()) 1911f5207b7SJohn Levon return; 1921f5207b7SJohn Levon 1931f5207b7SJohn Levon if (!expr) 1941f5207b7SJohn Levon return; 1951f5207b7SJohn Levon if (!is_freed(expr)) 1961f5207b7SJohn Levon return; 1971f5207b7SJohn Levon 1981f5207b7SJohn Levon name = expr_to_var(expr); 1991f5207b7SJohn Levon sm_warning("returning freed memory '%s'", name); 2001f5207b7SJohn Levon set_state_expr(my_id, expr, &ok); 2011f5207b7SJohn Levon free_string(name); 2021f5207b7SJohn Levon } 2031f5207b7SJohn Levon 2041f5207b7SJohn Levon static void match_free(const char *fn, struct expression *expr, void *param) 2051f5207b7SJohn Levon { 2061f5207b7SJohn Levon struct expression *arg; 2071f5207b7SJohn Levon 2081f5207b7SJohn Levon if (is_impossible_path()) 2091f5207b7SJohn Levon return; 2101f5207b7SJohn Levon 2111f5207b7SJohn Levon arg = get_argument_from_call_expr(expr->args, PTR_INT(param)); 2121f5207b7SJohn Levon if (!arg) 2131f5207b7SJohn Levon return; 2141f5207b7SJohn Levon if (is_freed(arg)) { 2151f5207b7SJohn Levon char *name = expr_to_var(arg); 2161f5207b7SJohn Levon 2171f5207b7SJohn Levon sm_error("double free of '%s'", name); 2181f5207b7SJohn Levon free_string(name); 2191f5207b7SJohn Levon } 2201f5207b7SJohn Levon set_state_expr(my_id, arg, &freed); 2211f5207b7SJohn Levon } 2221f5207b7SJohn Levon 2231f5207b7SJohn Levon static void set_param_freed(struct expression *expr, int param, char *key, char *value) 2241f5207b7SJohn Levon { 2251f5207b7SJohn Levon struct expression *arg; 2261f5207b7SJohn Levon char *name; 2271f5207b7SJohn Levon struct symbol *sym; 2281f5207b7SJohn Levon struct sm_state *sm; 2291f5207b7SJohn Levon 2301f5207b7SJohn Levon while (expr->type == EXPR_ASSIGNMENT) 2311f5207b7SJohn Levon expr = strip_expr(expr->right); 2321f5207b7SJohn Levon if (expr->type != EXPR_CALL) 2331f5207b7SJohn Levon return; 2341f5207b7SJohn Levon 2351f5207b7SJohn Levon arg = get_argument_from_call_expr(expr->args, param); 2361f5207b7SJohn Levon if (!arg) 2371f5207b7SJohn Levon return; 2381f5207b7SJohn Levon name = get_variable_from_key(arg, key, &sym); 2391f5207b7SJohn Levon if (!name || !sym) 2401f5207b7SJohn Levon goto free; 2411f5207b7SJohn Levon 2421f5207b7SJohn Levon if (!is_impossible_path()) { 2431f5207b7SJohn Levon sm = get_sm_state(my_id, name, sym); 2441f5207b7SJohn Levon if (sm && slist_has_state(sm->possible, &freed)) { 2451f5207b7SJohn Levon sm_warning("'%s' double freed", name); 2461f5207b7SJohn Levon set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */ 2471f5207b7SJohn Levon } 2481f5207b7SJohn Levon } 2491f5207b7SJohn Levon 2501f5207b7SJohn Levon set_state(my_id, name, sym, &freed); 2511f5207b7SJohn Levon free: 2521f5207b7SJohn Levon free_string(name); 2531f5207b7SJohn Levon } 2541f5207b7SJohn Levon 2551f5207b7SJohn Levon int parent_is_free_var_sym_strict(const char *name, struct symbol *sym) 2561f5207b7SJohn Levon { 2571f5207b7SJohn Levon char buf[256]; 2581f5207b7SJohn Levon char *start; 2591f5207b7SJohn Levon char *end; 2601f5207b7SJohn Levon struct smatch_state *state; 2611f5207b7SJohn Levon 2621f5207b7SJohn Levon strncpy(buf, name, sizeof(buf) - 1); 2631f5207b7SJohn Levon buf[sizeof(buf) - 1] = '\0'; 2641f5207b7SJohn Levon 2651f5207b7SJohn Levon start = &buf[0]; 2661f5207b7SJohn Levon while ((*start == '&')) 2671f5207b7SJohn Levon start++; 2681f5207b7SJohn Levon 2691f5207b7SJohn Levon while ((end = strrchr(start, '-'))) { 2701f5207b7SJohn Levon *end = '\0'; 2711f5207b7SJohn Levon state = __get_state(my_id, start, sym); 2721f5207b7SJohn Levon if (state == &freed) 2731f5207b7SJohn Levon return 1; 2741f5207b7SJohn Levon } 2751f5207b7SJohn Levon return 0; 2761f5207b7SJohn Levon } 2771f5207b7SJohn Levon 2781f5207b7SJohn Levon int parent_is_free_strict(struct expression *expr) 2791f5207b7SJohn Levon { 2801f5207b7SJohn Levon struct symbol *sym; 2811f5207b7SJohn Levon char *var; 2821f5207b7SJohn Levon int ret = 0; 2831f5207b7SJohn Levon 2841f5207b7SJohn Levon expr = strip_expr(expr); 2851f5207b7SJohn Levon var = expr_to_var_sym(expr, &sym); 2861f5207b7SJohn Levon if (!var || !sym) 2871f5207b7SJohn Levon goto free; 2881f5207b7SJohn Levon ret = parent_is_free_var_sym_strict(var, sym); 2891f5207b7SJohn Levon free: 2901f5207b7SJohn Levon free_string(var); 2911f5207b7SJohn Levon return ret; 2921f5207b7SJohn Levon } 2931f5207b7SJohn Levon 294*efe51d0cSJohn Levon static void match_untracked(struct expression *call, int param) 295*efe51d0cSJohn Levon { 296*efe51d0cSJohn Levon struct state_list *slist = NULL; 297*efe51d0cSJohn Levon struct expression *arg; 298*efe51d0cSJohn Levon struct sm_state *sm; 299*efe51d0cSJohn Levon char *name; 300*efe51d0cSJohn Levon char buf[64]; 301*efe51d0cSJohn Levon int len; 302*efe51d0cSJohn Levon 303*efe51d0cSJohn Levon arg = get_argument_from_call_expr(call->args, param); 304*efe51d0cSJohn Levon if (!arg) 305*efe51d0cSJohn Levon return; 306*efe51d0cSJohn Levon 307*efe51d0cSJohn Levon name = expr_to_var(arg); 308*efe51d0cSJohn Levon if (!name) 309*efe51d0cSJohn Levon return; 310*efe51d0cSJohn Levon snprintf(buf, sizeof(buf), "%s->", name); 311*efe51d0cSJohn Levon free_string(name); 312*efe51d0cSJohn Levon len = strlen(buf); 313*efe51d0cSJohn Levon 314*efe51d0cSJohn Levon FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 315*efe51d0cSJohn Levon if (strncmp(sm->name, buf, len) == 0) 316*efe51d0cSJohn Levon add_ptr_list(&slist, sm); 317*efe51d0cSJohn Levon } END_FOR_EACH_SM(sm); 318*efe51d0cSJohn Levon 319*efe51d0cSJohn Levon FOR_EACH_PTR(slist, sm) { 320*efe51d0cSJohn Levon set_state(sm->owner, sm->name, sm->sym, &ok); 321*efe51d0cSJohn Levon } END_FOR_EACH_PTR(sm); 322*efe51d0cSJohn Levon 323*efe51d0cSJohn Levon free_slist(&slist); 324*efe51d0cSJohn Levon } 325*efe51d0cSJohn Levon 3261f5207b7SJohn Levon void check_free_strict(int id) 3271f5207b7SJohn Levon { 3281f5207b7SJohn Levon my_id = id; 3291f5207b7SJohn Levon 3301f5207b7SJohn Levon if (option_project != PROJ_KERNEL) 3311f5207b7SJohn Levon return; 3321f5207b7SJohn Levon 3331f5207b7SJohn Levon add_function_hook("kfree", &match_free, INT_PTR(0)); 3341f5207b7SJohn Levon add_function_hook("kmem_cache_free", &match_free, INT_PTR(1)); 3351f5207b7SJohn Levon 3361f5207b7SJohn Levon if (option_spammy) 3371f5207b7SJohn Levon add_hook(&match_symbol, SYM_HOOK); 3381f5207b7SJohn Levon add_hook(&match_dereferences, DEREF_HOOK); 3391f5207b7SJohn Levon add_hook(&match_call, FUNCTION_CALL_HOOK); 3401f5207b7SJohn Levon add_hook(&match_return, RETURN_HOOK); 3411f5207b7SJohn Levon 3421f5207b7SJohn Levon add_modification_hook_late(my_id, &ok_to_use); 3431f5207b7SJohn Levon add_pre_merge_hook(my_id, &pre_merge_hook); 3441f5207b7SJohn Levon 3451f5207b7SJohn Levon select_return_states_hook(PARAM_FREED, &set_param_freed); 346*efe51d0cSJohn Levon add_untracked_param_hook(&match_untracked); 3471f5207b7SJohn Levon } 348