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 * check_memory() is getting too big and messy. 20 * 21 */ 22 23 #include <string.h> 24 #include "smatch.h" 25 #include "smatch_slist.h" 26 #include "smatch_extra.h" 27 28 static int my_id; 29 30 STATE(freed); 31 STATE(ok); 32 33 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr) 34 { 35 if (sm->state != &ok) 36 set_state(my_id, sm->name, sm->sym, &ok); 37 } 38 39 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other) 40 { 41 if (is_impossible_path()) 42 set_state(my_id, cur->name, cur->sym, &ok); 43 } 44 45 static struct smatch_state *unmatched_state(struct sm_state *sm) 46 { 47 struct smatch_state *state; 48 sval_t sval; 49 50 if (sm->state != &freed) 51 return &undefined; 52 53 state = get_state(SMATCH_EXTRA, sm->name, sm->sym); 54 if (!state) 55 return &undefined; 56 if (!estate_get_single_value(state, &sval) || sval.value != 0) 57 return &undefined; 58 /* It makes it easier to consider NULL pointers as freed. */ 59 return &freed; 60 } 61 62 static int is_freed(struct expression *expr) 63 { 64 struct sm_state *sm; 65 66 sm = get_sm_state_expr(my_id, expr); 67 if (sm && slist_has_state(sm->possible, &freed)) 68 return 1; 69 return 0; 70 } 71 72 static void match_symbol(struct expression *expr) 73 { 74 struct expression *parent; 75 char *name; 76 77 if (is_impossible_path()) 78 return; 79 if (__in_fake_parameter_assign) 80 return; 81 82 parent = expr_get_parent_expr(expr); 83 while (parent && parent->type == EXPR_PREOP && parent->op == '(') 84 parent = expr_get_parent_expr(parent); 85 if (parent && parent->type == EXPR_PREOP && parent->op == '&') 86 return; 87 88 if (!is_freed(expr)) 89 return; 90 name = expr_to_var(expr); 91 sm_warning("'%s' was already freed.", name); 92 free_string(name); 93 } 94 95 static void match_dereferences(struct expression *expr) 96 { 97 char *name; 98 99 if (expr->type != EXPR_PREOP) 100 return; 101 102 if (is_impossible_path()) 103 return; 104 if (__in_fake_parameter_assign) 105 return; 106 107 expr = strip_expr(expr->unop); 108 if (!is_freed(expr)) 109 return; 110 name = expr_to_var(expr); 111 sm_error("dereferencing freed memory '%s'", name); 112 set_state_expr(my_id, expr, &ok); 113 free_string(name); 114 } 115 116 static int ignored_params[16]; 117 118 static void set_ignored_params(struct expression *call) 119 { 120 struct expression *arg; 121 const char *p; 122 int i; 123 124 memset(&ignored_params, 0, sizeof(ignored_params)); 125 126 i = -1; 127 FOR_EACH_PTR(call->args, arg) { 128 i++; 129 if (arg->type != EXPR_STRING) 130 continue; 131 goto found; 132 } END_FOR_EACH_PTR(arg); 133 134 return; 135 136 found: 137 i++; 138 p = arg->string->data; 139 while ((p = strchr(p, '%'))) { 140 if (i >= ARRAY_SIZE(ignored_params)) 141 return; 142 p++; 143 if (*p == '%') { 144 p++; 145 continue; 146 } 147 if (*p == '.') 148 p++; 149 if (*p == '*') 150 i++; 151 if (*p == 'p') 152 ignored_params[i] = 1; 153 i++; 154 } 155 } 156 157 static int is_free_func(struct expression *fn) 158 { 159 char *name; 160 int ret = 0; 161 162 name = expr_to_str(fn); 163 if (!name) 164 return 0; 165 if (strstr(name, "free")) 166 ret = 1; 167 free_string(name); 168 169 return ret; 170 } 171 172 static void match_call(struct expression *expr) 173 { 174 struct expression *arg; 175 char *name; 176 int i; 177 178 if (is_impossible_path()) 179 return; 180 181 set_ignored_params(expr); 182 183 i = -1; 184 FOR_EACH_PTR(expr->args, arg) { 185 i++; 186 if (!is_pointer(arg)) 187 continue; 188 if (!is_freed(arg)) 189 continue; 190 if (ignored_params[i]) 191 continue; 192 193 name = expr_to_var(arg); 194 if (is_free_func(expr->fn)) 195 sm_error("double free of '%s'", name); 196 else 197 sm_warning("passing freed memory '%s'", name); 198 set_state_expr(my_id, arg, &ok); 199 free_string(name); 200 } END_FOR_EACH_PTR(arg); 201 } 202 203 static void match_return(struct expression *expr) 204 { 205 char *name; 206 207 if (is_impossible_path()) 208 return; 209 210 if (!expr) 211 return; 212 if (!is_freed(expr)) 213 return; 214 215 name = expr_to_var(expr); 216 sm_warning("returning freed memory '%s'", name); 217 set_state_expr(my_id, expr, &ok); 218 free_string(name); 219 } 220 221 static void match_free(const char *fn, struct expression *expr, void *param) 222 { 223 struct expression *arg; 224 225 if (is_impossible_path()) 226 return; 227 228 arg = get_argument_from_call_expr(expr->args, PTR_INT(param)); 229 if (!arg) 230 return; 231 if (is_freed(arg)) { 232 char *name = expr_to_var(arg); 233 234 sm_error("double free of '%s'", name); 235 free_string(name); 236 } 237 set_state_expr(my_id, arg, &freed); 238 } 239 240 static void set_param_freed(struct expression *expr, int param, char *key, char *value) 241 { 242 struct expression *arg; 243 char *name; 244 struct symbol *sym; 245 struct sm_state *sm; 246 247 while (expr->type == EXPR_ASSIGNMENT) 248 expr = strip_expr(expr->right); 249 if (expr->type != EXPR_CALL) 250 return; 251 252 arg = get_argument_from_call_expr(expr->args, param); 253 if (!arg) 254 return; 255 name = get_variable_from_key(arg, key, &sym); 256 if (!name || !sym) 257 goto free; 258 259 if (!is_impossible_path()) { 260 sm = get_sm_state(my_id, name, sym); 261 if (sm && slist_has_state(sm->possible, &freed)) { 262 sm_warning("'%s' double freed", name); 263 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */ 264 } 265 } 266 267 set_state(my_id, name, sym, &freed); 268 free: 269 free_string(name); 270 } 271 272 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym) 273 { 274 char buf[256]; 275 char *start; 276 char *end; 277 struct smatch_state *state; 278 279 strncpy(buf, name, sizeof(buf) - 1); 280 buf[sizeof(buf) - 1] = '\0'; 281 282 start = &buf[0]; 283 while ((*start == '&')) 284 start++; 285 286 while ((end = strrchr(start, '-'))) { 287 *end = '\0'; 288 state = __get_state(my_id, start, sym); 289 if (state == &freed) 290 return 1; 291 } 292 return 0; 293 } 294 295 int parent_is_free_strict(struct expression *expr) 296 { 297 struct symbol *sym; 298 char *var; 299 int ret = 0; 300 301 expr = strip_expr(expr); 302 var = expr_to_var_sym(expr, &sym); 303 if (!var || !sym) 304 goto free; 305 ret = parent_is_free_var_sym_strict(var, sym); 306 free: 307 free_string(var); 308 return ret; 309 } 310 311 static void match_untracked(struct expression *call, int param) 312 { 313 struct state_list *slist = NULL; 314 struct expression *arg; 315 struct sm_state *sm; 316 char *name; 317 char buf[64]; 318 int len; 319 320 arg = get_argument_from_call_expr(call->args, param); 321 if (!arg) 322 return; 323 324 name = expr_to_var(arg); 325 if (!name) 326 return; 327 snprintf(buf, sizeof(buf), "%s->", name); 328 free_string(name); 329 len = strlen(buf); 330 331 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 332 if (strncmp(sm->name, buf, len) == 0) 333 add_ptr_list(&slist, sm); 334 } END_FOR_EACH_SM(sm); 335 336 FOR_EACH_PTR(slist, sm) { 337 set_state(sm->owner, sm->name, sm->sym, &ok); 338 } END_FOR_EACH_PTR(sm); 339 340 free_slist(&slist); 341 } 342 343 void check_free_strict(int id) 344 { 345 my_id = id; 346 347 if (option_project != PROJ_KERNEL) 348 return; 349 350 add_function_hook("kfree", &match_free, INT_PTR(0)); 351 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1)); 352 353 if (option_spammy) 354 add_hook(&match_symbol, SYM_HOOK); 355 add_hook(&match_dereferences, DEREF_HOOK); 356 add_hook(&match_call, FUNCTION_CALL_HOOK); 357 add_hook(&match_return, RETURN_HOOK); 358 359 add_modification_hook_late(my_id, &ok_to_use); 360 add_pre_merge_hook(my_id, &pre_merge_hook); 361 add_unmatched_state_hook(my_id, &unmatched_state); 362 363 select_return_states_hook(PARAM_FREED, &set_param_freed); 364 add_untracked_param_hook(&match_untracked); 365 } 366