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