ranges: fix a leak
[smatch.git] / check_free_strict.c
blob7c033076a1c37c9a3b1e1802423196be46fe25ee
1 /*
2 * Copyright (C) 2010 Dan Carpenter.
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.
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.
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
19 * check_memory() is getting too big and messy.
23 #include <string.h>
24 #include "smatch.h"
25 #include "smatch_slist.h"
27 static int my_id;
29 STATE(freed);
30 STATE(ok);
32 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
34 if (sm->state != &ok)
35 set_state(my_id, sm->name, sm->sym, &ok);
38 static void pre_merge_hook(struct sm_state *sm)
40 if (is_impossible_path())
41 set_state(my_id, sm->name, sm->sym, &ok);
44 static int is_freed(struct expression *expr)
46 struct sm_state *sm;
48 sm = get_sm_state_expr(my_id, expr);
49 if (sm && slist_has_state(sm->possible, &freed))
50 return 1;
51 return 0;
54 static void match_symbol(struct expression *expr)
56 struct expression *parent;
57 char *name;
59 if (is_impossible_path())
60 return;
62 parent = expr_get_parent_expr(expr);
63 while (parent && parent->type == EXPR_PREOP && parent->op == '(')
64 parent = expr_get_parent_expr(parent);
65 if (parent && parent->type == EXPR_PREOP && parent->op == '&')
66 return;
68 if (!is_freed(expr))
69 return;
70 name = expr_to_var(expr);
71 sm_msg("warn: '%s' was already freed.", name);
72 free_string(name);
75 static void match_dereferences(struct expression *expr)
77 char *name;
79 if (expr->type != EXPR_PREOP)
80 return;
82 if (is_impossible_path())
83 return;
85 expr = strip_expr(expr->unop);
86 if (!is_freed(expr))
87 return;
88 name = expr_to_var(expr);
89 sm_msg("error: dereferencing freed memory '%s'", name);
90 set_state_expr(my_id, expr, &ok);
91 free_string(name);
94 static int ignored_params[16];
96 static void set_ignored_params(struct expression *call)
98 struct expression *arg;
99 const char *p;
100 int i;
102 memset(&ignored_params, 0, sizeof(ignored_params));
104 i = -1;
105 FOR_EACH_PTR(call->args, arg) {
106 i++;
107 if (arg->type != EXPR_STRING)
108 continue;
109 goto found;
110 } END_FOR_EACH_PTR(arg);
112 return;
114 found:
115 i++;
116 p = arg->string->data;
117 while ((p = strchr(p, '%'))) {
118 if (i >= ARRAY_SIZE(ignored_params))
119 return;
120 p++;
121 if (*p == '%') {
122 p++;
123 continue;
125 if (*p == '.')
126 p++;
127 if (*p == '*')
128 i++;
129 if (*p == 'p')
130 ignored_params[i] = 1;
131 i++;
135 static int is_free_func(struct expression *fn)
137 char *name;
138 int ret = 0;
140 name = expr_to_str(fn);
141 if (!name)
142 return 0;
143 if (strstr(name, "free"))
144 ret = 1;
145 free_string(name);
147 return ret;
150 static void match_call(struct expression *expr)
152 struct expression *arg;
153 char *name;
154 int i;
156 if (is_impossible_path())
157 return;
159 set_ignored_params(expr);
161 i = -1;
162 FOR_EACH_PTR(expr->args, arg) {
163 i++;
164 if (!is_pointer(arg))
165 continue;
166 if (!is_freed(arg))
167 continue;
168 if (ignored_params[i])
169 continue;
171 name = expr_to_var(arg);
172 if (is_free_func(expr->fn))
173 sm_msg("error: double free of '%s'", name);
174 else
175 sm_msg("warn: passing freed memory '%s'", name);
176 set_state_expr(my_id, arg, &ok);
177 free_string(name);
178 } END_FOR_EACH_PTR(arg);
181 static void match_return(struct expression *expr)
183 char *name;
185 if (is_impossible_path())
186 return;
188 if (!expr)
189 return;
190 if (!is_freed(expr))
191 return;
193 name = expr_to_var(expr);
194 sm_msg("warn: returning freed memory '%s'", name);
195 set_state_expr(my_id, expr, &ok);
196 free_string(name);
199 static void match_free(const char *fn, struct expression *expr, void *param)
201 struct expression *arg;
203 if (is_impossible_path())
204 return;
206 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
207 if (!arg)
208 return;
209 if (is_freed(arg)) {
210 char *name = expr_to_var(arg);
212 sm_msg("error: double free of '%s'", name);
213 free_string(name);
215 set_state_expr(my_id, arg, &freed);
218 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
220 struct expression *arg;
221 char *name;
222 struct symbol *sym;
223 struct sm_state *sm;
225 while (expr->type == EXPR_ASSIGNMENT)
226 expr = strip_expr(expr->right);
227 if (expr->type != EXPR_CALL)
228 return;
230 arg = get_argument_from_call_expr(expr->args, param);
231 if (!arg)
232 return;
233 name = get_variable_from_key(arg, key, &sym);
234 if (!name || !sym)
235 goto free;
237 if (!is_impossible_path()) {
238 sm = get_sm_state(my_id, name, sym);
239 if (sm && slist_has_state(sm->possible, &freed)) {
240 sm_msg("warn: '%s' double freed", name);
241 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */
245 set_state(my_id, name, sym, &freed);
246 free:
247 free_string(name);
250 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
252 char buf[256];
253 char *start;
254 char *end;
255 struct smatch_state *state;
257 strncpy(buf, name, sizeof(buf) - 1);
258 buf[sizeof(buf) - 1] = '\0';
260 start = &buf[0];
261 while ((*start == '&'))
262 start++;
264 while ((end = strrchr(start, '-'))) {
265 *end = '\0';
266 state = __get_state(my_id, start, sym);
267 if (state == &freed)
268 return 1;
270 return 0;
273 int parent_is_free_strict(struct expression *expr)
275 struct symbol *sym;
276 char *var;
277 int ret = 0;
279 expr = strip_expr(expr);
280 var = expr_to_var_sym(expr, &sym);
281 if (!var || !sym)
282 goto free;
283 ret = parent_is_free_var_sym_strict(var, sym);
284 free:
285 free_string(var);
286 return ret;
289 void check_free_strict(int id)
291 my_id = id;
293 if (option_project != PROJ_KERNEL)
294 return;
296 add_function_hook("kfree", &match_free, INT_PTR(0));
297 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
299 if (option_spammy)
300 add_hook(&match_symbol, SYM_HOOK);
301 add_hook(&match_dereferences, DEREF_HOOK);
302 add_hook(&match_call, FUNCTION_CALL_HOOK);
303 add_hook(&match_return, RETURN_HOOK);
305 add_modification_hook_late(my_id, &ok_to_use);
306 add_pre_merge_hook(my_id, &pre_merge_hook);
308 select_return_states_hook(PARAM_FREED, &set_param_freed);