1 /*
2 * Copyright (C) 2008 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 #include <fcntl.h>
19 #include <unistd.h>
20 #include "parse.h"
21 #include "smatch.h"
22 #include "smatch_slist.h"
23
24 static void check_sm_is_leaked(struct sm_state *sm);
25
26 static int my_id;
27
28 STATE(allocated);
29 STATE(assigned);
30 STATE(isfree);
31 STATE(malloced);
32 STATE(isnull);
33 STATE(unfree);
34
35 /*
36 malloced --> allocated --> assigned --> isfree +
37 \-> isnull. \-> isfree +
38
39 isfree --> unfree.
40 \-> isnull.
41 */
42
43 static struct tracker_list *arguments;
44
45 static const char *allocation_funcs[] = {
46 "malloc",
47 "kmalloc",
48 "kzalloc",
49 NULL,
50 };
51
52 static char *get_parent_name(struct symbol *sym)
53 {
54 static char buf[256];
55
56 if (!sym || !sym->ident)
57 return NULL;
58
59 snprintf(buf, 255, "-%s", sym->ident->name);
60 buf[255] = '\0';
61 return alloc_string(buf);
62 }
63
64 static int is_parent_sym(const char *name)
65 {
66 if (!strncmp(name, "-", 1))
67 return 1;
68 return 0;
69 }
70
71 static int is_complex(struct expression *expr)
72 {
73 char *name;
74 int ret = 1;
75
76 name = expr_to_var(expr);
77 if (name)
78 ret = 0;
79 free_string(name);
80 return ret;
81 }
82
83 static struct smatch_state *unmatched_state(struct sm_state *sm)
84 {
85 if (is_parent_sym(sm->name))
86 return &assigned;
87 return &undefined;
88 }
89
90 static void assign_parent(struct symbol *sym)
91 {
92 char *name;
93
94 name = get_parent_name(sym);
95 if (!name)
96 return;
97 set_state(my_id, name, sym, &assigned);
98 free_string(name);
99 }
100
101 static int parent_is_assigned(struct symbol *sym)
102 {
103 struct smatch_state *state;
104 char *name;
105
106 name = get_parent_name(sym);
107 if (!name)
108 return 0;
109 state = get_state(my_id, name, sym);
110 free_string(name);
111 if (state == &assigned)
112 return 1;
113 return 0;
114 }
115
116 static int is_allocation(struct expression *expr)
117 {
118 char *fn_name;
119 int i;
120
121 if (expr->type != EXPR_CALL)
122 return 0;
123
124 if (!(fn_name = expr_to_var_sym(expr->fn, NULL)))
125 return 0;
126
127 for (i = 0; allocation_funcs[i]; i++) {
128 if (!strcmp(fn_name, allocation_funcs[i])) {
129 free_string(fn_name);
130 return 1;
131 }
132 }
133 free_string(fn_name);
134 return 0;
135 }
136
137 static int is_freed(const char *name, struct symbol *sym)
138 {
139 struct state_list *slist;
140
141 slist = get_possible_states(my_id, name, sym);
142 if (slist_has_state(slist, &isfree)) {
143 return 1;
144 }
145 return 0;
146 }
147
148 static int is_argument(struct symbol *sym)
149 {
150 struct tracker *arg;
151
152 FOR_EACH_PTR(arguments, arg) {
153 if (arg->sym == sym)
154 return 1;
155 } END_FOR_EACH_PTR(arg);
156 return 0;
157 }
158
159 static void match_function_def(struct symbol *sym)
160 {
161 struct symbol *arg;
162
163 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
164 add_tracker(&arguments, my_id, (arg->ident?arg->ident->name:"NULL"), arg);
165 } END_FOR_EACH_PTR(arg);
166 }
167
168 static int is_parent(struct expression *expr)
169 {
170 if (expr->type == EXPR_DEREF)
171 return 0;
172 return 1;
173 }
174
175 static void match_assign(struct expression *expr)
176 {
177 struct expression *left, *right;
178 char *left_name = NULL;
179 char *right_name = NULL;
180 struct symbol *left_sym, *right_sym;
181 struct smatch_state *state;
182 struct state_list *slist;
183 struct sm_state *tmp;
184
185 left = strip_expr(expr->left);
186 left_name = expr_to_str_sym(left, &left_sym);
187
188 right = strip_expr(expr->right);
189 while (right->type == EXPR_ASSIGNMENT)
190 right = right->left;
191
192 if (left_name && left_sym && is_allocation(right) &&
193 !(left_sym->ctype.modifiers &
194 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
195 !parent_is_assigned(left_sym)) {
196 set_state(my_id, left_name, left_sym, &malloced);
197 goto exit;
198 }
199
200 right_name = expr_to_str_sym(right, &right_sym);
201
202 if (right_name && (state = get_state(my_id, right_name, right_sym))) {
203 if (state == &isfree && !is_complex(right))
204 sm_error("assigning freed pointer '%s'", right_name);
205 set_state(my_id, right_name, right_sym, &assigned);
206 }
207
208 if (is_zero(expr->right)) {
209 slist = get_possible_states(my_id, left_name, left_sym);
210
211 FOR_EACH_PTR(slist, tmp) {
212 check_sm_is_leaked(tmp);
213 } END_FOR_EACH_PTR(tmp);
214 }
215
216 if (is_freed(left_name, left_sym)) {
217 set_state(my_id, left_name, left_sym, &unfree);
218 }
219 if (left_name && is_parent(left))
220 assign_parent(left_sym);
221 if (right_name && is_parent(right))
222 assign_parent(right_sym);
223 exit:
224 free_string(left_name);
225 free_string(right_name);
226 }
227
228 static int is_null(const char *name, struct symbol *sym)
229 {
230 struct smatch_state *state;
231
232 state = get_state(my_id, name, sym);
233 if (state && !strcmp(state->name, "isnull"))
234 return 1;
235 return 0;
236 }
237
238 static void set_unfree(struct sm_state *sm, struct expression *mod_expr)
239 {
240 if (slist_has_state(sm->possible, &isfree))
241 set_state(my_id, sm->name, sm->sym, &unfree);
242 }
243
244 static void match_free_func(const char *fn, struct expression *expr, void *data)
245 {
246 struct expression *ptr_expr;
247 char *ptr_name;
248 struct symbol *ptr_sym;
249 int arg_num = PTR_INT(data);
250
251 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
252 ptr_name = expr_to_var_sym(ptr_expr, &ptr_sym);
253 if (!ptr_name)
254 return;
255 set_state(my_id, ptr_name, ptr_sym, &isfree);
256 free_string(ptr_name);
257 }
258
259 static int possibly_allocated(struct state_list *slist)
260 {
261 struct sm_state *tmp;
262
263 FOR_EACH_PTR(slist, tmp) {
264 if (tmp->state == &allocated)
265 return 1;
266 if (tmp->state == &malloced)
267 return 1;
268 } END_FOR_EACH_PTR(tmp);
269 return 0;
270 }
271
272 static void check_sm_is_leaked(struct sm_state *sm)
273 {
274 if (possibly_allocated(sm->possible) &&
275 !is_null(sm->name, sm->sym) &&
276 !is_argument(sm->sym) &&
277 !parent_is_assigned(sm->sym))
278 sm_error("memory leak of '%s'", sm->name);
279 }
280
281 static void check_tracker_is_leaked(struct tracker *t)
282 {
283 struct sm_state *sm;
284
285 sm = get_sm_state(t->owner, t->name, t->sym);
286 if (sm)
287 check_sm_is_leaked(sm);
288 __free_tracker(t);
289 }
290
291 static void match_declarations(struct symbol *sym)
292 {
293 const char *name;
294
295 if ((get_base_type(sym))->type == SYM_ARRAY) {
296 return;
297 }
298
299 name = sym->ident->name;
300
301 if (sym->initializer) {
302 if (is_allocation(sym->initializer)) {
303 set_state(my_id, name, sym, &malloced);
304 add_scope_hook((scope_hook *)&check_tracker_is_leaked,
305 alloc_tracker(my_id, name, sym));
306 scoped_state(my_id, name, sym);
307 } else {
308 assign_parent(sym);
309 }
310 }
311 }
312
313 static void check_for_allocated(void)
314 {
315 struct stree *stree;
316 struct sm_state *tmp;
317
318 stree = __get_cur_stree();
319 FOR_EACH_MY_SM(my_id, stree, tmp) {
320 check_sm_is_leaked(tmp);
321 } END_FOR_EACH_SM(tmp);
322 }
323
324 static void match_return(struct expression *ret_value)
325 {
326 char *name;
327 struct symbol *sym;
328
329 if (__inline_fn)
330 return;
331 name = expr_to_str_sym(ret_value, &sym);
332 if (sym)
333 assign_parent(sym);
334 free_string(name);
335 check_for_allocated();
336 }
337
338 static void set_new_true_false_paths(const char *name, struct symbol *sym)
339 {
340 struct smatch_state *tmp;
341
342 tmp = get_state(my_id, name, sym);
343
344 if (!tmp) {
345 return;
346 }
347
348 if (tmp == &isfree) {
349 sm_warning("why do you care about freed memory? '%s'", name);
350 }
351
352 if (tmp == &assigned) {
353 /* we don't care about assigned pointers any more */
354 return;
355 }
356 set_true_false_states(my_id, name, sym, &allocated, &isnull);
357 }
358
359 static void match_condition(struct expression *expr)
360 {
361 struct symbol *sym;
362 char *name;
363
364 expr = strip_expr(expr);
365 switch (expr->type) {
366 case EXPR_PREOP:
367 case EXPR_SYMBOL:
368 case EXPR_DEREF:
369 name = expr_to_var_sym(expr, &sym);
370 if (!name)
371 return;
372 set_new_true_false_paths(name, sym);
373 free_string(name);
374 return;
375 case EXPR_ASSIGNMENT:
376 /* You have to deal with stuff like if (a = b = c) */
377 match_condition(expr->right);
378 match_condition(expr->left);
379 return;
380 default:
381 return;
382 }
383 }
384
385 static void match_function_call(struct expression *expr)
386 {
387 struct expression *tmp;
388 struct symbol *sym;
389 char *name;
390 struct sm_state *state;
391
392 FOR_EACH_PTR(expr->args, tmp) {
393 tmp = strip_expr(tmp);
394 name = expr_to_str_sym(tmp, &sym);
395 if (!name)
396 continue;
397 if ((state = get_sm_state(my_id, name, sym))) {
398 if (possibly_allocated(state->possible)) {
399 set_state(my_id, name, sym, &assigned);
400 }
401 }
402 assign_parent(sym);
403 free_string(name);
404 } END_FOR_EACH_PTR(tmp);
405 }
406
407 static void match_end_func(struct symbol *sym)
408 {
409 if (__inline_fn)
410 return;
411 check_for_allocated();
412 }
413
414 static void match_after_func(struct symbol *sym)
415 {
416 if (__inline_fn)
417 return;
418 free_trackers_and_list(&arguments);
419 }
420
421 static void register_funcs_from_file(void)
422 {
423 struct token *token;
424 const char *func;
425 int arg;
426
427 token = get_tokens_file("kernel.frees_argument");
428 if (!token)
429 return;
430 if (token_type(token) != TOKEN_STREAMBEGIN)
431 return;
432 token = token->next;
433 while (token_type(token) != TOKEN_STREAMEND) {
434 if (token_type(token) != TOKEN_IDENT)
435 return;
436 func = show_ident(token->ident);
437 token = token->next;
438 if (token_type(token) != TOKEN_NUMBER)
439 return;
440 arg = atoi(token->number);
441 add_function_hook(func, &match_free_func, INT_PTR(arg));
442 token = token->next;
443 }
444 clear_token_alloc();
445 }
446
447 void check_memory(int id)
448 {
449 my_id = id;
450 add_unmatched_state_hook(my_id, &unmatched_state);
451 add_hook(&match_function_def, FUNC_DEF_HOOK);
452 add_hook(&match_declarations, DECLARATION_HOOK);
453 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
454 add_hook(&match_condition, CONDITION_HOOK);
455 add_hook(&match_assign, ASSIGNMENT_HOOK);
456 add_hook(&match_return, RETURN_HOOK);
457 add_hook(&match_end_func, END_FUNC_HOOK);
458 add_hook(&match_after_func, AFTER_FUNC_HOOK);
459 add_modification_hook(my_id, &set_unfree);
460 if (option_project == PROJ_KERNEL) {
461 add_function_hook("kfree", &match_free_func, (void *)0);
462 register_funcs_from_file();
463 } else {
464 add_function_hook("free", &match_free_func, (void *)0);
465 }
466 }