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