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