extra/db: silence an invalid SQL warning
[smatch.git] / check_free_strict.c
blob769a030c84d1386e34db0141b4a683c923e164a4
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;
87 if (__in_fake_parameter_assign)
88 return;
90 expr = strip_expr(expr->unop);
91 if (!is_freed(expr))
92 return;
93 name = expr_to_var(expr);
94 sm_msg("error: dereferencing freed memory '%s'", name);
95 set_state_expr(my_id, expr, &ok);
96 free_string(name);
99 static int ignored_params[16];
101 static void set_ignored_params(struct expression *call)
103 struct expression *arg;
104 const char *p;
105 int i;
107 memset(&ignored_params, 0, sizeof(ignored_params));
109 i = -1;
110 FOR_EACH_PTR(call->args, arg) {
111 i++;
112 if (arg->type != EXPR_STRING)
113 continue;
114 goto found;
115 } END_FOR_EACH_PTR(arg);
117 return;
119 found:
120 i++;
121 p = arg->string->data;
122 while ((p = strchr(p, '%'))) {
123 if (i >= ARRAY_SIZE(ignored_params))
124 return;
125 p++;
126 if (*p == '%') {
127 p++;
128 continue;
130 if (*p == '.')
131 p++;
132 if (*p == '*')
133 i++;
134 if (*p == 'p')
135 ignored_params[i] = 1;
136 i++;
140 static int is_free_func(struct expression *fn)
142 char *name;
143 int ret = 0;
145 name = expr_to_str(fn);
146 if (!name)
147 return 0;
148 if (strstr(name, "free"))
149 ret = 1;
150 free_string(name);
152 return ret;
155 static void match_call(struct expression *expr)
157 struct expression *arg;
158 char *name;
159 int i;
161 if (is_impossible_path())
162 return;
164 set_ignored_params(expr);
166 i = -1;
167 FOR_EACH_PTR(expr->args, arg) {
168 i++;
169 if (!is_pointer(arg))
170 continue;
171 if (!is_freed(arg))
172 continue;
173 if (ignored_params[i])
174 continue;
176 name = expr_to_var(arg);
177 if (is_free_func(expr->fn))
178 sm_msg("error: double free of '%s'", name);
179 else
180 sm_msg("warn: passing freed memory '%s'", name);
181 set_state_expr(my_id, arg, &ok);
182 free_string(name);
183 } END_FOR_EACH_PTR(arg);
186 static void match_return(struct expression *expr)
188 char *name;
190 if (is_impossible_path())
191 return;
193 if (!expr)
194 return;
195 if (!is_freed(expr))
196 return;
198 name = expr_to_var(expr);
199 sm_msg("warn: returning freed memory '%s'", name);
200 set_state_expr(my_id, expr, &ok);
201 free_string(name);
204 static void match_free(const char *fn, struct expression *expr, void *param)
206 struct expression *arg;
208 if (is_impossible_path())
209 return;
211 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
212 if (!arg)
213 return;
214 if (is_freed(arg)) {
215 char *name = expr_to_var(arg);
217 sm_msg("error: double free of '%s'", name);
218 free_string(name);
220 set_state_expr(my_id, arg, &freed);
223 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
225 struct expression *arg;
226 char *name;
227 struct symbol *sym;
228 struct sm_state *sm;
230 while (expr->type == EXPR_ASSIGNMENT)
231 expr = strip_expr(expr->right);
232 if (expr->type != EXPR_CALL)
233 return;
235 arg = get_argument_from_call_expr(expr->args, param);
236 if (!arg)
237 return;
238 name = get_variable_from_key(arg, key, &sym);
239 if (!name || !sym)
240 goto free;
242 if (!is_impossible_path()) {
243 sm = get_sm_state(my_id, name, sym);
244 if (sm && slist_has_state(sm->possible, &freed)) {
245 sm_msg("warn: '%s' double freed", name);
246 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */
250 set_state(my_id, name, sym, &freed);
251 free:
252 free_string(name);
255 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
257 char buf[256];
258 char *start;
259 char *end;
260 struct smatch_state *state;
262 strncpy(buf, name, sizeof(buf) - 1);
263 buf[sizeof(buf) - 1] = '\0';
265 start = &buf[0];
266 while ((*start == '&'))
267 start++;
269 while ((end = strrchr(start, '-'))) {
270 *end = '\0';
271 state = __get_state(my_id, start, sym);
272 if (state == &freed)
273 return 1;
275 return 0;
278 int parent_is_free_strict(struct expression *expr)
280 struct symbol *sym;
281 char *var;
282 int ret = 0;
284 expr = strip_expr(expr);
285 var = expr_to_var_sym(expr, &sym);
286 if (!var || !sym)
287 goto free;
288 ret = parent_is_free_var_sym_strict(var, sym);
289 free:
290 free_string(var);
291 return ret;
294 void check_free_strict(int id)
296 my_id = id;
298 if (option_project != PROJ_KERNEL)
299 return;
301 add_function_hook("kfree", &match_free, INT_PTR(0));
302 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
304 if (option_spammy)
305 add_hook(&match_symbol, SYM_HOOK);
306 add_hook(&match_dereferences, DEREF_HOOK);
307 add_hook(&match_call, FUNCTION_CALL_HOOK);
308 add_hook(&match_return, RETURN_HOOK);
310 add_modification_hook_late(my_id, &ok_to_use);
311 add_pre_merge_hook(my_id, &pre_merge_hook);
313 select_return_states_hook(PARAM_FREED, &set_param_freed);