Use $(CFLAGS) when compiling smatch.c itself.
[smatch.git] / check_free_strict.c
blob31e3369417a336981a643a0ec5b3629a4121d066
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_msg("warn: '%s' was already freed.", name);
75 free_string(name);
78 static void match_dereferences(struct expression *expr)
80 char *name;
82 if (expr->type != EXPR_PREOP)
83 return;
85 if (is_impossible_path())
86 return;
88 expr = strip_expr(expr->unop);
89 if (!is_freed(expr))
90 return;
91 name = expr_to_var(expr);
92 sm_msg("error: dereferencing freed memory '%s'", name);
93 set_state_expr(my_id, expr, &ok);
94 free_string(name);
97 static int ignored_params[16];
99 static void set_ignored_params(struct expression *call)
101 struct expression *arg;
102 const char *p;
103 int i;
105 memset(&ignored_params, 0, sizeof(ignored_params));
107 i = -1;
108 FOR_EACH_PTR(call->args, arg) {
109 i++;
110 if (arg->type != EXPR_STRING)
111 continue;
112 goto found;
113 } END_FOR_EACH_PTR(arg);
115 return;
117 found:
118 i++;
119 p = arg->string->data;
120 while ((p = strchr(p, '%'))) {
121 if (i >= ARRAY_SIZE(ignored_params))
122 return;
123 p++;
124 if (*p == '%') {
125 p++;
126 continue;
128 if (*p == '.')
129 p++;
130 if (*p == '*')
131 i++;
132 if (*p == 'p')
133 ignored_params[i] = 1;
134 i++;
138 static int is_free_func(struct expression *fn)
140 char *name;
141 int ret = 0;
143 name = expr_to_str(fn);
144 if (!name)
145 return 0;
146 if (strstr(name, "free"))
147 ret = 1;
148 free_string(name);
150 return ret;
153 static void match_call(struct expression *expr)
155 struct expression *arg;
156 char *name;
157 int i;
159 if (is_impossible_path())
160 return;
162 set_ignored_params(expr);
164 i = -1;
165 FOR_EACH_PTR(expr->args, arg) {
166 i++;
167 if (!is_pointer(arg))
168 continue;
169 if (!is_freed(arg))
170 continue;
171 if (ignored_params[i])
172 continue;
174 name = expr_to_var(arg);
175 if (is_free_func(expr->fn))
176 sm_msg("error: double free of '%s'", name);
177 else
178 sm_msg("warn: passing freed memory '%s'", name);
179 set_state_expr(my_id, arg, &ok);
180 free_string(name);
181 } END_FOR_EACH_PTR(arg);
184 static void match_return(struct expression *expr)
186 char *name;
188 if (is_impossible_path())
189 return;
191 if (!expr)
192 return;
193 if (!is_freed(expr))
194 return;
196 name = expr_to_var(expr);
197 sm_msg("warn: returning freed memory '%s'", name);
198 set_state_expr(my_id, expr, &ok);
199 free_string(name);
202 static void match_free(const char *fn, struct expression *expr, void *param)
204 struct expression *arg;
206 if (is_impossible_path())
207 return;
209 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
210 if (!arg)
211 return;
212 if (is_freed(arg)) {
213 char *name = expr_to_var(arg);
215 sm_msg("error: double free of '%s'", name);
216 free_string(name);
218 set_state_expr(my_id, arg, &freed);
221 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
223 struct expression *arg;
224 char *name;
225 struct symbol *sym;
226 struct sm_state *sm;
228 while (expr->type == EXPR_ASSIGNMENT)
229 expr = strip_expr(expr->right);
230 if (expr->type != EXPR_CALL)
231 return;
233 arg = get_argument_from_call_expr(expr->args, param);
234 if (!arg)
235 return;
236 name = get_variable_from_key(arg, key, &sym);
237 if (!name || !sym)
238 goto free;
240 if (!is_impossible_path()) {
241 sm = get_sm_state(my_id, name, sym);
242 if (sm && slist_has_state(sm->possible, &freed)) {
243 sm_msg("warn: '%s' double freed", name);
244 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */
248 set_state(my_id, name, sym, &freed);
249 free:
250 free_string(name);
253 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
255 char buf[256];
256 char *start;
257 char *end;
258 struct smatch_state *state;
260 strncpy(buf, name, sizeof(buf) - 1);
261 buf[sizeof(buf) - 1] = '\0';
263 start = &buf[0];
264 while ((*start == '&'))
265 start++;
267 while ((end = strrchr(start, '-'))) {
268 *end = '\0';
269 state = __get_state(my_id, start, sym);
270 if (state == &freed)
271 return 1;
273 return 0;
276 int parent_is_free_strict(struct expression *expr)
278 struct symbol *sym;
279 char *var;
280 int ret = 0;
282 expr = strip_expr(expr);
283 var = expr_to_var_sym(expr, &sym);
284 if (!var || !sym)
285 goto free;
286 ret = parent_is_free_var_sym_strict(var, sym);
287 free:
288 free_string(var);
289 return ret;
292 void check_free_strict(int id)
294 my_id = id;
296 if (option_project != PROJ_KERNEL)
297 return;
299 add_function_hook("kfree", &match_free, INT_PTR(0));
300 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
302 if (option_spammy)
303 add_hook(&match_symbol, SYM_HOOK);
304 add_hook(&match_dereferences, DEREF_HOOK);
305 add_hook(&match_call, FUNCTION_CALL_HOOK);
306 add_hook(&match_return, RETURN_HOOK);
308 add_modification_hook_late(my_id, &ok_to_use);
309 add_pre_merge_hook(my_id, &pre_merge_hook);
311 select_return_states_hook(PARAM_FREED, &set_param_freed);