slist: a pointer is equal to itself in cmp_possible_sm()
[smatch.git] / check_free.c
blob44df17e0d3408480de962bb1e2664e3cf3fb02f2
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"
26 #include "smatch_extra.h"
28 static int my_id;
30 STATE(freed);
31 STATE(ok);
33 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
35 if (sm->state != &ok)
36 set_state(my_id, sm->name, sm->sym, &ok);
39 static void pre_merge_hook(struct sm_state *sm)
41 if (is_impossible_path())
42 set_state(my_id, sm->name, sm->sym, &ok);
45 static int is_freed(struct expression *expr)
47 struct sm_state *sm;
49 sm = get_sm_state_expr(my_id, expr);
50 if (sm && slist_has_state(sm->possible, &freed))
51 return 1;
52 return 0;
55 static void match_symbol(struct expression *expr)
57 struct expression *parent;
58 char *name;
60 if (is_impossible_path())
61 return;
62 if (__in_fake_parameter_assign)
63 return;
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;
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);
78 static void match_dereferences(struct expression *expr)
80 char *name;
82 if (__in_fake_parameter_assign)
83 return;
85 if (expr->type != EXPR_PREOP)
86 return;
88 if (is_impossible_path())
89 return;
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);
100 static int ignored_params[16];
102 static void set_ignored_params(struct expression *call)
104 struct expression *arg;
105 const char *p;
106 int i;
108 memset(&ignored_params, 0, sizeof(ignored_params));
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);
118 return;
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;
131 if (*p == '.')
132 p++;
133 if (*p == '*')
134 i++;
135 if (*p == 'p')
136 ignored_params[i] = 1;
137 i++;
141 static int is_free_func(struct expression *fn)
143 char *name;
144 int ret = 0;
146 name = expr_to_str(fn);
147 if (!name)
148 return 0;
149 if (strstr(name, "free"))
150 ret = 1;
151 free_string(name);
153 return ret;
156 static void match_call(struct expression *expr)
158 struct expression *arg;
159 char *name;
160 int i;
162 if (is_impossible_path())
163 return;
165 set_ignored_params(expr);
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;
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);
187 static void match_return(struct expression *expr)
189 char *name;
191 if (is_impossible_path())
192 return;
193 if (__in_fake_parameter_assign)
194 return;
196 if (!expr)
197 return;
198 if (!is_freed(expr))
199 return;
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);
207 static void match_free(const char *fn, struct expression *expr, void *param)
209 struct expression *arg;
211 if (is_impossible_path())
212 return;
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);
220 sm_error("double free of '%s'", name);
221 free_string(name);
223 set_state_expr(my_id, arg, &freed);
226 static void set_param_freed(struct expression *call, struct expression *arg, char *key, char *unused)
228 struct symbol *sym;
229 char *name;
231 name = get_variable_from_key(arg, key, &sym);
232 if (!name || !sym)
233 goto free;
235 set_state(my_id, name, sym, &freed);
236 free:
237 free_string(name);
240 int parent_is_free_var_sym(const char *name, struct symbol *sym)
242 char buf[256];
243 char *start;
244 char *end;
245 struct smatch_state *state;
247 if (option_project == PROJ_KERNEL)
248 return parent_is_free_var_sym_strict(name, sym);
250 strncpy(buf, name, sizeof(buf) - 1);
251 buf[sizeof(buf) - 1] = '\0';
253 start = &buf[0];
254 while ((*start == '&'))
255 start++;
257 while ((end = strrchr(start, '-'))) {
258 *end = '\0';
259 state = __get_state(my_id, start, sym);
260 if (state == &freed)
261 return 1;
263 return 0;
266 int parent_is_free(struct expression *expr)
268 struct symbol *sym;
269 char *var;
270 int ret = 0;
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;
282 void check_free(int id)
284 my_id = id;
286 if (option_project == PROJ_KERNEL) {
287 /* The kernel use check_free_strict.c */
288 return;
291 add_function_hook("free", &match_free, INT_PTR(0));
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);
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);