buf_size: ignore certain assignments
[smatch.git] / check_free_strict.c
bloba8c224f10d326515396fb9be6e136556f755d961
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 *cur, struct sm_state *other)
41 if (is_impossible_path())
42 set_state(my_id, cur->name, cur->sym, &ok);
45 static struct smatch_state *unmatched_state(struct sm_state *sm)
47 struct smatch_state *state;
48 sval_t sval;
50 if (sm->state != &freed)
51 return &undefined;
53 state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
54 if (!state)
55 return &undefined;
56 if (!estate_get_single_value(state, &sval) || sval.value != 0)
57 return &undefined;
58 /* It makes it easier to consider NULL pointers as freed. */
59 return &freed;
62 static int is_freed(struct expression *expr)
64 struct sm_state *sm;
66 sm = get_sm_state_expr(my_id, expr);
67 if (sm && slist_has_state(sm->possible, &freed))
68 return 1;
69 return 0;
72 static void match_symbol(struct expression *expr)
74 struct expression *parent;
75 char *name;
77 if (is_impossible_path())
78 return;
79 if (__in_fake_parameter_assign)
80 return;
82 parent = expr_get_parent_expr(expr);
83 while (parent && parent->type == EXPR_PREOP && parent->op == '(')
84 parent = expr_get_parent_expr(parent);
85 if (parent && parent->type == EXPR_PREOP && parent->op == '&')
86 return;
88 if (!is_freed(expr))
89 return;
90 name = expr_to_var(expr);
91 sm_warning("'%s' was already freed.", name);
92 free_string(name);
95 static void match_dereferences(struct expression *expr)
97 char *name;
99 if (expr->type != EXPR_PREOP)
100 return;
102 if (is_impossible_path())
103 return;
104 if (__in_fake_parameter_assign)
105 return;
107 expr = strip_expr(expr->unop);
108 if (!is_freed(expr))
109 return;
110 name = expr_to_var(expr);
111 sm_error("dereferencing freed memory '%s'", name);
112 set_state_expr(my_id, expr, &ok);
113 free_string(name);
116 static int ignored_params[16];
118 static void set_ignored_params(struct expression *call)
120 struct expression *arg;
121 const char *p;
122 int i;
124 memset(&ignored_params, 0, sizeof(ignored_params));
126 i = -1;
127 FOR_EACH_PTR(call->args, arg) {
128 i++;
129 if (arg->type != EXPR_STRING)
130 continue;
131 goto found;
132 } END_FOR_EACH_PTR(arg);
134 return;
136 found:
137 i++;
138 p = arg->string->data;
139 while ((p = strchr(p, '%'))) {
140 if (i >= ARRAY_SIZE(ignored_params))
141 return;
142 p++;
143 if (*p == '%') {
144 p++;
145 continue;
147 if (*p == '.')
148 p++;
149 if (*p == '*')
150 i++;
151 if (*p == 'p')
152 ignored_params[i] = 1;
153 i++;
157 static int is_free_func(struct expression *fn)
159 char *name;
160 int ret = 0;
162 name = expr_to_str(fn);
163 if (!name)
164 return 0;
165 if (strstr(name, "free"))
166 ret = 1;
167 free_string(name);
169 return ret;
172 static void match_call(struct expression *expr)
174 struct expression *arg;
175 char *name;
176 int i;
178 if (is_impossible_path())
179 return;
181 set_ignored_params(expr);
183 i = -1;
184 FOR_EACH_PTR(expr->args, arg) {
185 i++;
186 if (!is_pointer(arg))
187 continue;
188 if (!is_freed(arg))
189 continue;
190 if (ignored_params[i])
191 continue;
193 name = expr_to_var(arg);
194 if (is_free_func(expr->fn))
195 sm_error("double free of '%s'", name);
196 else
197 sm_warning("passing freed memory '%s'", name);
198 set_state_expr(my_id, arg, &ok);
199 free_string(name);
200 } END_FOR_EACH_PTR(arg);
203 static void match_return(struct expression *expr)
205 char *name;
207 if (is_impossible_path())
208 return;
210 if (!expr)
211 return;
212 if (!is_freed(expr))
213 return;
215 name = expr_to_var(expr);
216 sm_warning("returning freed memory '%s'", name);
217 set_state_expr(my_id, expr, &ok);
218 free_string(name);
221 static void match_free(const char *fn, struct expression *expr, void *param)
223 struct expression *arg;
225 if (is_impossible_path())
226 return;
228 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
229 if (!arg)
230 return;
231 if (is_freed(arg)) {
232 char *name = expr_to_var(arg);
234 sm_error("double free of '%s'", name);
235 free_string(name);
237 set_state_expr(my_id, arg, &freed);
240 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
242 struct expression *arg;
243 char *name;
244 struct symbol *sym;
245 struct sm_state *sm;
247 while (expr->type == EXPR_ASSIGNMENT)
248 expr = strip_expr(expr->right);
249 if (expr->type != EXPR_CALL)
250 return;
252 arg = get_argument_from_call_expr(expr->args, param);
253 if (!arg)
254 return;
255 name = get_variable_from_key(arg, key, &sym);
256 if (!name || !sym)
257 goto free;
259 if (!is_impossible_path()) {
260 sm = get_sm_state(my_id, name, sym);
261 if (sm && slist_has_state(sm->possible, &freed)) {
262 sm_warning("'%s' double freed", name);
263 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */
267 set_state(my_id, name, sym, &freed);
268 free:
269 free_string(name);
272 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
274 char buf[256];
275 char *start;
276 char *end;
277 struct smatch_state *state;
279 strncpy(buf, name, sizeof(buf) - 1);
280 buf[sizeof(buf) - 1] = '\0';
282 start = &buf[0];
283 while ((*start == '&'))
284 start++;
286 while ((end = strrchr(start, '-'))) {
287 *end = '\0';
288 state = __get_state(my_id, start, sym);
289 if (state == &freed)
290 return 1;
292 return 0;
295 int parent_is_free_strict(struct expression *expr)
297 struct symbol *sym;
298 char *var;
299 int ret = 0;
301 expr = strip_expr(expr);
302 var = expr_to_var_sym(expr, &sym);
303 if (!var || !sym)
304 goto free;
305 ret = parent_is_free_var_sym_strict(var, sym);
306 free:
307 free_string(var);
308 return ret;
311 static void match_untracked(struct expression *call, int param)
313 struct state_list *slist = NULL;
314 struct expression *arg;
315 struct sm_state *sm;
316 char *name;
317 char buf[64];
318 int len;
320 arg = get_argument_from_call_expr(call->args, param);
321 if (!arg)
322 return;
324 name = expr_to_var(arg);
325 if (!name)
326 return;
327 snprintf(buf, sizeof(buf), "%s->", name);
328 free_string(name);
329 len = strlen(buf);
331 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
332 if (strncmp(sm->name, buf, len) == 0)
333 add_ptr_list(&slist, sm);
334 } END_FOR_EACH_SM(sm);
336 FOR_EACH_PTR(slist, sm) {
337 set_state(sm->owner, sm->name, sm->sym, &ok);
338 } END_FOR_EACH_PTR(sm);
340 free_slist(&slist);
343 void check_free_strict(int id)
345 my_id = id;
347 if (option_project != PROJ_KERNEL)
348 return;
350 add_function_hook("kfree", &match_free, INT_PTR(0));
351 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
353 if (option_spammy)
354 add_hook(&match_symbol, SYM_HOOK);
355 add_hook(&match_dereferences, DEREF_HOOK);
356 add_hook(&match_call, FUNCTION_CALL_HOOK);
357 add_hook(&match_return, RETURN_HOOK);
359 add_modification_hook_late(my_id, &ok_to_use);
360 add_pre_merge_hook(my_id, &pre_merge_hook);
361 add_unmatched_state_hook(my_id, &unmatched_state);
363 select_return_states_hook(PARAM_FREED, &set_param_freed);
364 add_untracked_param_hook(&match_untracked);