param_key: fix container of when no struct member is referenced
[smatch.git] / check_free.c
blob3640fef629019c5fbc6f067ad3cd0548a062661d
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 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 if (is_part_of_condition(expr))
66 return;
68 parent = expr_get_parent_expr(expr);
69 while (parent && parent->type == EXPR_PREOP && parent->op == '(')
70 parent = expr_get_parent_expr(parent);
71 if (parent && parent->type == EXPR_PREOP && parent->op == '&')
72 return;
74 if (!is_freed(expr))
75 return;
77 if (is_percent_p_print(expr))
78 return;
80 name = expr_to_var(expr);
81 sm_warning("'%s' was already freed.", name);
82 free_string(name);
85 static void match_dereferences(struct expression *expr)
87 char *name;
89 if (__in_fake_parameter_assign)
90 return;
92 if (expr->type != EXPR_PREOP)
93 return;
95 if (is_impossible_path())
96 return;
98 expr = strip_expr(expr->unop);
99 if (!is_freed(expr))
100 return;
101 name = expr_to_var(expr);
102 sm_error("dereferencing freed memory '%s'", name);
103 set_state_expr(my_id, expr, &ok);
104 free_string(name);
107 static int ignored_params[16];
109 static void set_ignored_params(struct expression *call)
111 struct expression *arg;
112 const char *p;
113 int i;
115 memset(&ignored_params, 0, sizeof(ignored_params));
117 i = -1;
118 FOR_EACH_PTR(call->args, arg) {
119 i++;
120 if (arg->type != EXPR_STRING)
121 continue;
122 goto found;
123 } END_FOR_EACH_PTR(arg);
125 return;
127 found:
128 i++;
129 p = arg->string->data;
130 while ((p = strchr(p, '%'))) {
131 if (i >= ARRAY_SIZE(ignored_params))
132 return;
133 p++;
134 if (*p == '%') {
135 p++;
136 continue;
138 if (*p == '.')
139 p++;
140 if (*p == '*')
141 i++;
142 if (*p == 'p')
143 ignored_params[i] = 1;
144 i++;
148 static int is_free_func(struct expression *fn)
150 char *name;
151 int ret = 0;
153 name = expr_to_str(fn);
154 if (!name)
155 return 0;
156 if (strstr(name, "free"))
157 ret = 1;
158 free_string(name);
160 return ret;
163 static void match_call(struct expression *expr)
165 struct expression *arg;
166 char *name;
167 int i;
169 if (is_impossible_path())
170 return;
172 set_ignored_params(expr);
174 i = -1;
175 FOR_EACH_PTR(expr->args, arg) {
176 i++;
177 if (!is_pointer(arg))
178 continue;
179 if (!is_freed(arg))
180 continue;
181 if (ignored_params[i])
182 continue;
183 if (is_percent_p_print(arg))
184 continue;
186 name = expr_to_var(arg);
187 if (is_free_func(expr->fn))
188 sm_error("double free of '%s'", name);
189 else
190 sm_warning("passing freed memory '%s'", name);
191 set_state_expr(my_id, arg, &ok);
192 free_string(name);
193 } END_FOR_EACH_PTR(arg);
196 static void match_return(struct expression *expr)
198 char *name;
200 if (is_impossible_path())
201 return;
202 if (__in_fake_parameter_assign)
203 return;
205 if (!expr)
206 return;
207 if (!is_freed(expr))
208 return;
210 name = expr_to_var(expr);
211 sm_warning("returning freed memory '%s'", name);
212 set_state_expr(my_id, expr, &ok);
213 free_string(name);
216 static void match_free(const char *fn, struct expression *expr, void *param)
218 struct expression *arg;
220 if (is_impossible_path())
221 return;
223 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
224 if (!arg)
225 return;
226 if (is_freed(arg)) {
227 char *name = expr_to_var(arg);
229 sm_error("double free of '%s'", name);
230 free_string(name);
232 set_state_expr(my_id, arg, &freed);
235 static void set_param_freed(struct expression *call, struct expression *arg, char *key, char *unused)
237 struct symbol *sym;
238 char *name;
240 name = get_variable_from_key(arg, key, &sym);
241 if (!name || !sym)
242 goto free;
244 set_state(my_id, name, sym, &freed);
245 free:
246 free_string(name);
249 int parent_is_free_var_sym(const char *name, struct symbol *sym)
251 char buf[256];
252 char *start;
253 char *end;
254 struct smatch_state *state;
255 char orig;
257 if (option_project == PROJ_KERNEL)
258 return parent_is_free_var_sym_strict(name, sym);
260 strncpy(buf, name, sizeof(buf) - 1);
261 buf[sizeof(buf) - 1] = '\0';
263 start = &buf[0];
264 while ((*start == '&'))
265 start++;
266 end = start;
267 while ((end = strrchr(end, '-'))) {
268 orig = *end;
269 *end = '\0';
270 state = __get_state(my_id, start, sym);
271 if (state == &freed)
272 return 1;
273 *end = orig;
274 end++;
276 return 0;
279 int parent_is_free(struct expression *expr)
281 struct symbol *sym;
282 char *var;
283 int ret = 0;
285 expr = strip_expr(expr);
286 var = expr_to_var_sym(expr, &sym);
287 if (!var || !sym)
288 goto free;
289 ret = parent_is_free_var_sym(var, sym);
290 free:
291 free_string(var);
292 return ret;
295 void check_free(int id)
297 my_id = id;
299 if (option_project == PROJ_KERNEL) {
300 /* The kernel use check_free_strict.c */
301 return;
304 add_function_hook("free", &match_free, INT_PTR(0));
306 if (option_spammy)
307 add_hook(&match_symbol, SYM_HOOK);
308 add_hook(&match_dereferences, DEREF_HOOK);
309 add_hook(&match_call, FUNCTION_CALL_HOOK);
310 add_hook(&match_return, RETURN_HOOK);
312 add_modification_hook(my_id, &ok_to_use);
313 select_return_implies_hook(PARAM_FREED, &set_param_freed);
314 add_pre_merge_hook(my_id, &pre_merge_hook);