1 /*
2 * Copyright (C) 2010 Dan Carpenter.
3 *
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.
8 *
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.
13 *
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
16 */
17
18 /*
19 * check_memory() is getting too big and messy.
20 *
21 */
22
23 #include <string.h>
24 #include "smatch.h"
25 #include "smatch_slist.h"
26 #include "smatch_extra.h"
27
28 static int my_id;
29
30 STATE(freed);
31 STATE(ok);
32
ok_to_use(struct sm_state * sm,struct expression * mod_expr)33 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
34 {
35 if (sm->state != &ok)
36 set_state(my_id, sm->name, sm->sym, &ok);
37 }
38
pre_merge_hook(struct sm_state * cur,struct sm_state * other)39 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
40 {
41 if (is_impossible_path())
42 set_state(my_id, cur->name, cur->sym, &ok);
43 }
44
unmatched_state(struct sm_state * sm)45 static struct smatch_state *unmatched_state(struct sm_state *sm)
46 {
47 struct smatch_state *state;
48 sval_t sval;
49
50 if (sm->state != &freed)
51 return &undefined;
52
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;
60 }
61
is_freed(struct expression * expr)62 static int is_freed(struct expression *expr)
63 {
64 struct sm_state *sm;
65
66 sm = get_sm_state_expr(my_id, expr);
67 if (sm && slist_has_state(sm->possible, &freed))
68 return 1;
69 return 0;
70 }
71
match_symbol(struct expression * expr)72 static void match_symbol(struct expression *expr)
73 {
74 struct expression *parent;
75 char *name;
76
77 if (is_impossible_path())
78 return;
79 if (__in_fake_parameter_assign)
80 return;
81
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;
87
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);
93 }
94
match_dereferences(struct expression * expr)95 static void match_dereferences(struct expression *expr)
96 {
97 char *name;
98
99 if (expr->type != EXPR_PREOP)
100 return;
101
102 if (is_impossible_path())
103 return;
104 if (__in_fake_parameter_assign)
105 return;
106
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);
114 }
115
116 static int ignored_params[16];
117
set_ignored_params(struct expression * call)118 static void set_ignored_params(struct expression *call)
119 {
120 struct expression *arg;
121 const char *p;
122 int i;
123
124 memset(&ignored_params, 0, sizeof(ignored_params));
125
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);
133
134 return;
135
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;
146 }
147 if (*p == '.')
148 p++;
149 if (*p == '*')
150 i++;
151 if (*p == 'p')
152 ignored_params[i] = 1;
153 i++;
154 }
155 }
156
is_free_func(struct expression * fn)157 static int is_free_func(struct expression *fn)
158 {
159 char *name;
160 int ret = 0;
161
162 name = expr_to_str(fn);
163 if (!name)
164 return 0;
165 if (strstr(name, "free"))
166 ret = 1;
167 free_string(name);
168
169 return ret;
170 }
171
match_call(struct expression * expr)172 static void match_call(struct expression *expr)
173 {
174 struct expression *arg;
175 char *name;
176 int i;
177
178 if (is_impossible_path())
179 return;
180
181 set_ignored_params(expr);
182
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;
192
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);
201 }
202
match_return(struct expression * expr)203 static void match_return(struct expression *expr)
204 {
205 char *name;
206
207 if (is_impossible_path())
208 return;
209
210 if (!expr)
211 return;
212 if (!is_freed(expr))
213 return;
214
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);
219 }
220
match_free(const char * fn,struct expression * expr,void * param)221 static void match_free(const char *fn, struct expression *expr, void *param)
222 {
223 struct expression *arg;
224
225 if (is_impossible_path())
226 return;
227
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);
233
234 sm_error("double free of '%s'", name);
235 free_string(name);
236 }
237 set_state_expr(my_id, arg, &freed);
238 }
239
set_param_freed(struct expression * expr,int param,char * key,char * value)240 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
241 {
242 struct expression *arg;
243 char *name;
244 struct symbol *sym;
245 struct sm_state *sm;
246
247 while (expr->type == EXPR_ASSIGNMENT)
248 expr = strip_expr(expr->right);
249 if (expr->type != EXPR_CALL)
250 return;
251
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;
258
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 */
264 }
265 }
266
267 set_state(my_id, name, sym, &freed);
268 free:
269 free_string(name);
270 }
271
parent_is_free_var_sym_strict(const char * name,struct symbol * sym)272 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
273 {
274 char buf[256];
275 char *start;
276 char *end;
277 struct smatch_state *state;
278
279 strncpy(buf, name, sizeof(buf) - 1);
280 buf[sizeof(buf) - 1] = '\0';
281
282 start = &buf[0];
283 while ((*start == '&'))
284 start++;
285
286 while ((end = strrchr(start, '-'))) {
287 *end = '\0';
288 state = __get_state(my_id, start, sym);
289 if (state == &freed)
290 return 1;
291 }
292 return 0;
293 }
294
parent_is_free_strict(struct expression * expr)295 int parent_is_free_strict(struct expression *expr)
296 {
297 struct symbol *sym;
298 char *var;
299 int ret = 0;
300
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;
309 }
310
match_untracked(struct expression * call,int param)311 static void match_untracked(struct expression *call, int param)
312 {
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;
319
320 arg = get_argument_from_call_expr(call->args, param);
321 if (!arg)
322 return;
323
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);
330
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);
335
336 FOR_EACH_PTR(slist, sm) {
337 set_state(sm->owner, sm->name, sm->sym, &ok);
338 } END_FOR_EACH_PTR(sm);
339
340 free_slist(&slist);
341 }
342
check_free_strict(int id)343 void check_free_strict(int id)
344 {
345 my_id = id;
346
347 if (option_project != PROJ_KERNEL)
348 return;
349
350 add_function_hook("kfree", &match_free, INT_PTR(0));
351 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
352
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);
358
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);
362
363 select_return_states_hook(PARAM_FREED, &set_param_freed);
364 add_untracked_param_hook(&match_untracked);
365 }
366