user_data2: if users can only specify a single value that's not a user rl
[smatch.git] / check_free.c
blobfc2e24cb953ca93e89a1472c72fb99b6fe36b956
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"
27 static int my_id;
29 STATE(freed);
30 STATE(ok);
32 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
34 if (sm->state != &ok)
35 set_state(my_id, sm->name, sm->sym, &ok);
38 static void pre_merge_hook(struct sm_state *sm)
40 if (is_impossible_path())
41 set_state(my_id, sm->name, sm->sym, &ok);
44 static int is_freed(struct expression *expr)
46 struct sm_state *sm;
48 sm = get_sm_state_expr(my_id, expr);
49 if (sm && slist_has_state(sm->possible, &freed))
50 return 1;
51 return 0;
54 static void match_symbol(struct expression *expr)
56 char *name;
58 if (is_impossible_path())
59 return;
61 if (!is_freed(expr))
62 return;
63 name = expr_to_var(expr);
64 sm_msg("warn: '%s' was already freed.", name);
65 free_string(name);
68 static void match_dereferences(struct expression *expr)
70 char *name;
72 if (expr->type != EXPR_PREOP)
73 return;
75 if (is_impossible_path())
76 return;
78 expr = strip_expr(expr->unop);
79 if (!is_freed(expr))
80 return;
81 name = expr_to_var(expr);
82 sm_msg("error: dereferencing freed memory '%s'", name);
83 set_state_expr(my_id, expr, &ok);
84 free_string(name);
87 static int ignored_params[16];
89 static void set_ignored_params(struct expression *call)
91 struct expression *arg;
92 const char *p;
93 int i;
95 memset(&ignored_params, 0, sizeof(ignored_params));
97 i = -1;
98 FOR_EACH_PTR(call->args, arg) {
99 i++;
100 if (arg->type != EXPR_STRING)
101 continue;
102 goto found;
103 } END_FOR_EACH_PTR(arg);
105 return;
107 found:
108 i++;
109 p = arg->string->data;
110 while ((p = strchr(p, '%'))) {
111 if (i >= ARRAY_SIZE(ignored_params))
112 return;
113 p++;
114 if (*p == '%') {
115 p++;
116 continue;
118 if (*p == '.')
119 p++;
120 if (*p == '*')
121 i++;
122 if (*p == 'p')
123 ignored_params[i] = 1;
124 i++;
128 static int is_free_func(struct expression *fn)
130 char *name;
131 int ret = 0;
133 name = expr_to_str(fn);
134 if (!name)
135 return 0;
136 if (strstr(name, "free"))
137 ret = 1;
138 free_string(name);
140 return ret;
143 static void match_call(struct expression *expr)
145 struct expression *arg;
146 char *name;
147 int i;
149 if (is_impossible_path())
150 return;
152 set_ignored_params(expr);
154 i = -1;
155 FOR_EACH_PTR(expr->args, arg) {
156 i++;
157 if (!is_pointer(arg))
158 continue;
159 if (!is_freed(arg))
160 continue;
161 if (ignored_params[i])
162 continue;
164 name = expr_to_var(arg);
165 if (is_free_func(expr->fn))
166 sm_msg("error: double free of '%s'", name);
167 else
168 sm_msg("warn: passing freed memory '%s'", name);
169 set_state_expr(my_id, arg, &ok);
170 free_string(name);
171 } END_FOR_EACH_PTR(arg);
174 static void match_return(struct expression *expr)
176 char *name;
178 if (is_impossible_path())
179 return;
181 if (!expr)
182 return;
183 if (!is_freed(expr))
184 return;
186 name = expr_to_var(expr);
187 sm_msg("warn: returning freed memory '%s'", name);
188 set_state_expr(my_id, expr, &ok);
189 free_string(name);
192 static void match_free(const char *fn, struct expression *expr, void *param)
194 struct expression *arg;
196 if (is_impossible_path())
197 return;
199 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
200 if (!arg)
201 return;
202 if (is_freed(arg)) {
203 char *name = expr_to_var(arg);
205 sm_msg("error: double free of '%s'", name);
206 free_string(name);
208 set_state_expr(my_id, arg, &freed);
211 static void set_param_freed(struct expression *arg, char *key, char *unused)
213 struct symbol *sym;
214 char *name;
216 name = get_variable_from_key(arg, key, &sym);
217 if (!name || !sym)
218 goto free;
220 set_state(my_id, name, sym, &freed);
221 free:
222 free_string(name);
225 int parent_is_free_var_sym(const char *name, struct symbol *sym)
227 char buf[256];
228 char *start;
229 char *end;
230 struct smatch_state *state;
232 strncpy(buf, name, sizeof(buf) - 1);
233 buf[sizeof(buf) - 1] = '\0';
235 start = &buf[0];
236 while ((*start == '&'))
237 start++;
239 while ((end = strrchr(start, '-'))) {
240 *end = '\0';
241 state = get_state(my_id, start, sym);
242 if (state == &freed)
243 return 1;
245 return 0;
248 int parent_is_free(struct expression *expr)
250 struct symbol *sym;
251 char *var;
252 int ret = 0;
254 expr = strip_expr(expr);
255 var = expr_to_var_sym(expr, &sym);
256 if (!var || !sym)
257 goto free;
258 ret = parent_is_free_var_sym(var, sym);
259 free:
260 free_string(var);
261 return ret;
264 void check_free(int id)
266 my_id = id;
268 if (option_project == PROJ_KERNEL) {
269 add_function_hook("kfree", &match_free, INT_PTR(0));
270 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
271 } else {
272 add_function_hook("free", &match_free, INT_PTR(0));
275 if (option_spammy)
276 add_hook(&match_symbol, SYM_HOOK);
277 add_hook(&match_dereferences, DEREF_HOOK);
278 add_hook(&match_call, FUNCTION_CALL_HOOK);
279 add_hook(&match_return, RETURN_HOOK);
281 add_modification_hook(my_id, &ok_to_use);
282 select_call_implies_hook(PARAM_FREED, &set_param_freed);
283 add_pre_merge_hook(my_id, &pre_merge_hook);