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 *sm) 40 { 41 if (is_impossible_path()) 42 set_state(my_id, sm->name, sm->sym, &ok); 43 } 44 45 static int is_freed(struct expression *expr) 46 { 47 struct sm_state *sm; 48 49 sm = get_sm_state_expr(my_id, expr); 50 if (sm && slist_has_state(sm->possible, &freed)) 51 return 1; 52 return 0; 53 } 54 55 static void match_symbol(struct expression *expr) 56 { 57 struct expression *parent; 58 char *name; 59 60 if (is_impossible_path()) 61 return; 62 if (__in_fake_parameter_assign) 63 return; 64 65 parent = expr_get_parent_expr(expr); 66 while (parent && parent->type == EXPR_PREOP && parent->op == '(') 67 parent = expr_get_parent_expr(parent); 68 if (parent && parent->type == EXPR_PREOP && parent->op == '&') 69 return; 70 71 if (!is_freed(expr)) 72 return; 73 name = expr_to_var(expr); 74 sm_warning("'%s' was already freed.", name); 75 free_string(name); 76 } 77 78 static void match_dereferences(struct expression *expr) 79 { 80 char *name; 81 82 if (expr->type != EXPR_PREOP) 83 return; 84 85 if (is_impossible_path()) 86 return; 87 if (__in_fake_parameter_assign) 88 return; 89 90 expr = strip_expr(expr->unop); 91 if (!is_freed(expr)) 92 return; 93 name = expr_to_var(expr); 94 sm_error("dereferencing freed memory '%s'", name); 95 set_state_expr(my_id, expr, &ok); 96 free_string(name); 97 } 98 99 static int ignored_params[16]; 100 101 static void set_ignored_params(struct expression *call) 102 { 103 struct expression *arg; 104 const char *p; 105 int i; 106 107 memset(&ignored_params, 0, sizeof(ignored_params)); 108 109 i = -1; 110 FOR_EACH_PTR(call->args, arg) { 111 i++; 112 if (arg->type != EXPR_STRING) 113 continue; 114 goto found; 115 } END_FOR_EACH_PTR(arg); 116 117 return; 118 119 found: 120 i++; 121 p = arg->string->data; 122 while ((p = strchr(p, '%'))) { 123 if (i >= ARRAY_SIZE(ignored_params)) 124 return; 125 p++; 126 if (*p == '%') { 127 p++; 128 continue; 129 } 130 if (*p == '.') 131 p++; 132 if (*p == '*') 133 i++; 134 if (*p == 'p') 135 ignored_params[i] = 1; 136 i++; 137 } 138 } 139 140 static int is_free_func(struct expression *fn) 141 { 142 char *name; 143 int ret = 0; 144 145 name = expr_to_str(fn); 146 if (!name) 147 return 0; 148 if (strstr(name, "free")) 149 ret = 1; 150 free_string(name); 151 152 return ret; 153 } 154 155 static void match_call(struct expression *expr) 156 { 157 struct expression *arg; 158 char *name; 159 int i; 160 161 if (is_impossible_path()) 162 return; 163 164 set_ignored_params(expr); 165 166 i = -1; 167 FOR_EACH_PTR(expr->args, arg) { 168 i++; 169 if (!is_pointer(arg)) 170 continue; 171 if (!is_freed(arg)) 172 continue; 173 if (ignored_params[i]) 174 continue; 175 176 name = expr_to_var(arg); 177 if (is_free_func(expr->fn)) 178 sm_error("double free of '%s'", name); 179 else 180 sm_warning("passing freed memory '%s'", name); 181 set_state_expr(my_id, arg, &ok); 182 free_string(name); 183 } END_FOR_EACH_PTR(arg); 184 } 185 186 static void match_return(struct expression *expr) 187 { 188 char *name; 189 190 if (is_impossible_path()) 191 return; 192 193 if (!expr) 194 return; 195 if (!is_freed(expr)) 196 return; 197 198 name = expr_to_var(expr); 199 sm_warning("returning freed memory '%s'", name); 200 set_state_expr(my_id, expr, &ok); 201 free_string(name); 202 } 203 204 static void match_free(const char *fn, struct expression *expr, void *param) 205 { 206 struct expression *arg; 207 208 if (is_impossible_path()) 209 return; 210 211 arg = get_argument_from_call_expr(expr->args, PTR_INT(param)); 212 if (!arg) 213 return; 214 if (is_freed(arg)) { 215 char *name = expr_to_var(arg); 216 217 sm_error("double free of '%s'", name); 218 free_string(name); 219 } 220 set_state_expr(my_id, arg, &freed); 221 } 222 223 static void set_param_freed(struct expression *expr, int param, char *key, char *value) 224 { 225 struct expression *arg; 226 char *name; 227 struct symbol *sym; 228 struct sm_state *sm; 229 230 while (expr->type == EXPR_ASSIGNMENT) 231 expr = strip_expr(expr->right); 232 if (expr->type != EXPR_CALL) 233 return; 234 235 arg = get_argument_from_call_expr(expr->args, param); 236 if (!arg) 237 return; 238 name = get_variable_from_key(arg, key, &sym); 239 if (!name || !sym) 240 goto free; 241 242 if (!is_impossible_path()) { 243 sm = get_sm_state(my_id, name, sym); 244 if (sm && slist_has_state(sm->possible, &freed)) { 245 sm_warning("'%s' double freed", name); 246 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */ 247 } 248 } 249 250 set_state(my_id, name, sym, &freed); 251 free: 252 free_string(name); 253 } 254 255 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym) 256 { 257 char buf[256]; 258 char *start; 259 char *end; 260 struct smatch_state *state; 261 262 strncpy(buf, name, sizeof(buf) - 1); 263 buf[sizeof(buf) - 1] = '\0'; 264 265 start = &buf[0]; 266 while ((*start == '&')) 267 start++; 268 269 while ((end = strrchr(start, '-'))) { 270 *end = '\0'; 271 state = __get_state(my_id, start, sym); 272 if (state == &freed) 273 return 1; 274 } 275 return 0; 276 } 277 278 int parent_is_free_strict(struct expression *expr) 279 { 280 struct symbol *sym; 281 char *var; 282 int ret = 0; 283 284 expr = strip_expr(expr); 285 var = expr_to_var_sym(expr, &sym); 286 if (!var || !sym) 287 goto free; 288 ret = parent_is_free_var_sym_strict(var, sym); 289 free: 290 free_string(var); 291 return ret; 292 } 293 294 void check_free_strict(int id) 295 { 296 my_id = id; 297 298 if (option_project != PROJ_KERNEL) 299 return; 300 301 add_function_hook("kfree", &match_free, INT_PTR(0)); 302 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1)); 303 304 if (option_spammy) 305 add_hook(&match_symbol, SYM_HOOK); 306 add_hook(&match_dereferences, DEREF_HOOK); 307 add_hook(&match_call, FUNCTION_CALL_HOOK); 308 add_hook(&match_return, RETURN_HOOK); 309 310 add_modification_hook_late(my_id, &ok_to_use); 311 add_pre_merge_hook(my_id, &pre_merge_hook); 312 313 select_return_states_hook(PARAM_FREED, &set_param_freed); 314 } 315