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 (__in_fake_parameter_assign) 83 return; 84 85 if (expr->type != EXPR_PREOP) 86 return; 87 88 if (is_impossible_path()) 89 return; 90 91 expr = strip_expr(expr->unop); 92 if (!is_freed(expr)) 93 return; 94 name = expr_to_var(expr); 95 sm_error("dereferencing freed memory '%s'", name); 96 set_state_expr(my_id, expr, &ok); 97 free_string(name); 98 } 99 100 static int ignored_params[16]; 101 102 static void set_ignored_params(struct expression *call) 103 { 104 struct expression *arg; 105 const char *p; 106 int i; 107 108 memset(&ignored_params, 0, sizeof(ignored_params)); 109 110 i = -1; 111 FOR_EACH_PTR(call->args, arg) { 112 i++; 113 if (arg->type != EXPR_STRING) 114 continue; 115 goto found; 116 } END_FOR_EACH_PTR(arg); 117 118 return; 119 120 found: 121 i++; 122 p = arg->string->data; 123 while ((p = strchr(p, '%'))) { 124 if (i >= ARRAY_SIZE(ignored_params)) 125 return; 126 p++; 127 if (*p == '%') { 128 p++; 129 continue; 130 } 131 if (*p == '.') 132 p++; 133 if (*p == '*') 134 i++; 135 if (*p == 'p') 136 ignored_params[i] = 1; 137 i++; 138 } 139 } 140 141 static int is_free_func(struct expression *fn) 142 { 143 char *name; 144 int ret = 0; 145 146 name = expr_to_str(fn); 147 if (!name) 148 return 0; 149 if (strstr(name, "free")) 150 ret = 1; 151 free_string(name); 152 153 return ret; 154 } 155 156 static void match_call(struct expression *expr) 157 { 158 struct expression *arg; 159 char *name; 160 int i; 161 162 if (is_impossible_path()) 163 return; 164 165 set_ignored_params(expr); 166 167 i = -1; 168 FOR_EACH_PTR(expr->args, arg) { 169 i++; 170 if (!is_pointer(arg)) 171 continue; 172 if (!is_freed(arg)) 173 continue; 174 if (ignored_params[i]) 175 continue; 176 177 name = expr_to_var(arg); 178 if (is_free_func(expr->fn)) 179 sm_error("double free of '%s'", name); 180 else 181 sm_warning("passing freed memory '%s'", name); 182 set_state_expr(my_id, arg, &ok); 183 free_string(name); 184 } END_FOR_EACH_PTR(arg); 185 } 186 187 static void match_return(struct expression *expr) 188 { 189 char *name; 190 191 if (is_impossible_path()) 192 return; 193 if (__in_fake_parameter_assign) 194 return; 195 196 if (!expr) 197 return; 198 if (!is_freed(expr)) 199 return; 200 201 name = expr_to_var(expr); 202 sm_warning("returning freed memory '%s'", name); 203 set_state_expr(my_id, expr, &ok); 204 free_string(name); 205 } 206 207 static void match_free(const char *fn, struct expression *expr, void *param) 208 { 209 struct expression *arg; 210 211 if (is_impossible_path()) 212 return; 213 214 arg = get_argument_from_call_expr(expr->args, PTR_INT(param)); 215 if (!arg) 216 return; 217 if (is_freed(arg)) { 218 char *name = expr_to_var(arg); 219 220 sm_error("double free of '%s'", name); 221 free_string(name); 222 } 223 set_state_expr(my_id, arg, &freed); 224 } 225 226 static void set_param_freed(struct expression *call, struct expression *arg, char *key, char *unused) 227 { 228 struct symbol *sym; 229 char *name; 230 231 name = get_variable_from_key(arg, key, &sym); 232 if (!name || !sym) 233 goto free; 234 235 set_state(my_id, name, sym, &freed); 236 free: 237 free_string(name); 238 } 239 240 int parent_is_free_var_sym(const char *name, struct symbol *sym) 241 { 242 char buf[256]; 243 char *start; 244 char *end; 245 struct smatch_state *state; 246 247 if (option_project == PROJ_KERNEL) 248 return parent_is_free_var_sym_strict(name, sym); 249 250 strncpy(buf, name, sizeof(buf) - 1); 251 buf[sizeof(buf) - 1] = '\0'; 252 253 start = &buf[0]; 254 while ((*start == '&')) 255 start++; 256 257 while ((end = strrchr(start, '-'))) { 258 *end = '\0'; 259 state = __get_state(my_id, start, sym); 260 if (state == &freed) 261 return 1; 262 } 263 return 0; 264 } 265 266 int parent_is_free(struct expression *expr) 267 { 268 struct symbol *sym; 269 char *var; 270 int ret = 0; 271 272 expr = strip_expr(expr); 273 var = expr_to_var_sym(expr, &sym); 274 if (!var || !sym) 275 goto free; 276 ret = parent_is_free_var_sym(var, sym); 277 free: 278 free_string(var); 279 return ret; 280 } 281 282 void check_free(int id) 283 { 284 my_id = id; 285 286 if (option_project == PROJ_KERNEL) { 287 /* The kernel use check_free_strict.c */ 288 return; 289 } 290 291 add_function_hook("free", &match_free, INT_PTR(0)); 292 293 if (option_spammy) 294 add_hook(&match_symbol, SYM_HOOK); 295 add_hook(&match_dereferences, DEREF_HOOK); 296 add_hook(&match_call, FUNCTION_CALL_HOOK); 297 add_hook(&match_return, RETURN_HOOK); 298 299 add_modification_hook(my_id, &ok_to_use); 300 select_return_implies_hook(PARAM_FREED, &set_param_freed); 301 add_pre_merge_hook(my_id, &pre_merge_hook); 302 } 303