project: fix parameter implications
[smatch.git] / check_memory.c
blob7ae13a22d05ff87ed61ddc1c12bcbb46f78a2c0c
1 /*
2 * sparse/check_memory.c
4 * Copyright (C) 2008 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include "parse.h"
13 #include "smatch.h"
14 #include "smatch_slist.h"
16 static void check_sm_is_leaked(struct sm_state *sm);
18 static int my_id;
20 STATE(allocated);
21 STATE(assigned);
22 STATE(isfree);
23 STATE(malloced);
24 STATE(isnull);
25 STATE(unfree);
28 malloced --> allocated --> assigned --> isfree +
29 \-> isnull. \-> isfree +
31 isfree --> unfree.
32 \-> isnull.
35 static struct tracker_list *arguments;
37 static const char *allocation_funcs[] = {
38 "malloc",
39 "kmalloc",
40 "kzalloc",
41 NULL,
44 static char *get_parent_name(struct symbol *sym)
46 static char buf[256];
48 if (!sym || !sym->ident)
49 return NULL;
51 snprintf(buf, 255, "-%s", sym->ident->name);
52 buf[255] = '\0';
53 return alloc_string(buf);
56 static int is_parent_sym(const char *name)
58 if (!strncmp(name, "-", 1))
59 return 1;
60 return 0;
63 static int is_complex(struct expression *expr)
65 char *name;
66 int ret = 1;
68 name = get_variable_from_expr(expr, NULL);
69 if (name)
70 ret = 0;
71 free_string(name);
72 return ret;
75 static struct smatch_state *unmatched_state(struct sm_state *sm)
77 if (is_parent_sym(sm->name))
78 return &assigned;
79 return &undefined;
82 static void assign_parent(struct symbol *sym)
84 char *name;
86 name = get_parent_name(sym);
87 if (!name)
88 return;
89 set_state(my_id, name, sym, &assigned);
90 free_string(name);
93 static int parent_is_assigned(struct symbol *sym)
95 struct smatch_state *state;
96 char *name;
98 name = get_parent_name(sym);
99 if (!name)
100 return 0;
101 state = get_state(my_id, name, sym);
102 free_string(name);
103 if (state == &assigned)
104 return 1;
105 return 0;
108 static int is_allocation(struct expression *expr)
110 char *fn_name;
111 int i;
113 if (expr->type != EXPR_CALL)
114 return 0;
116 if (!(fn_name = get_variable_from_expr(expr->fn, NULL)))
117 return 0;
119 for (i = 0; allocation_funcs[i]; i++) {
120 if (!strcmp(fn_name, allocation_funcs[i])) {
121 free_string(fn_name);
122 return 1;
125 free_string(fn_name);
126 return 0;
129 static int is_freed(const char *name, struct symbol *sym)
131 struct state_list *slist;
133 slist = get_possible_states(my_id, name, sym);
134 if (slist_has_state(slist, &isfree)) {
135 return 1;
137 return 0;
140 static int is_argument(struct symbol *sym)
142 struct tracker *arg;
144 FOR_EACH_PTR(arguments, arg) {
145 if (arg->sym == sym)
146 return 1;
147 } END_FOR_EACH_PTR(arg);
148 return 0;
151 static void match_function_def(struct symbol *sym)
153 struct symbol *arg;
155 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
156 add_tracker(&arguments, my_id, (arg->ident?arg->ident->name:"NULL"), arg);
157 } END_FOR_EACH_PTR(arg);
160 static int is_parent(struct expression *expr)
162 if (expr->type == EXPR_DEREF)
163 return 0;
164 return 1;
167 static void match_assign(struct expression *expr)
169 struct expression *left, *right;
170 char *left_name = NULL;
171 char *right_name = NULL;
172 struct symbol *left_sym, *right_sym;
173 struct smatch_state *state;
174 struct state_list *slist;
175 struct sm_state *tmp;
177 left = strip_expr(expr->left);
178 left_name = get_variable_from_expr_complex(left, &left_sym);
180 right = strip_expr(expr->right);
181 while (right->type == EXPR_ASSIGNMENT)
182 right = right->left;
184 if (left_name && left_sym && is_allocation(right) &&
185 !(left_sym->ctype.modifiers &
186 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
187 !parent_is_assigned(left_sym)) {
188 set_state(my_id, left_name, left_sym, &malloced);
189 goto exit;
192 right_name = get_variable_from_expr_complex(right, &right_sym);
194 if (right_name && (state = get_state(my_id, right_name, right_sym))) {
195 if (state == &isfree && !is_complex(right))
196 sm_msg("error: assigning freed pointer '%s'", right_name);
197 set_state(my_id, right_name, right_sym, &assigned);
200 if (is_zero(expr->right)) {
201 slist = get_possible_states(my_id, left_name, left_sym);
203 FOR_EACH_PTR(slist, tmp) {
204 check_sm_is_leaked(tmp);
205 } END_FOR_EACH_PTR(tmp);
208 if (is_freed(left_name, left_sym)) {
209 set_state(my_id, left_name, left_sym, &unfree);
211 if (left_name && is_parent(left))
212 assign_parent(left_sym);
213 if (right_name && is_parent(right))
214 assign_parent(right_sym);
215 exit:
216 free_string(left_name);
217 free_string(right_name);
220 static int is_null(const char *name, struct symbol *sym)
222 struct smatch_state *state;
224 state = get_state(my_id, name, sym);
225 if (state && !strcmp(state->name, "isnull"))
226 return 1;
227 return 0;
230 static void set_unfree(const char *name, struct symbol *sym, struct expression *expr, void *unused)
232 set_state(my_id, name, sym, &unfree);
235 static void match_free_func(const char *fn, struct expression *expr, void *data)
237 struct expression *ptr_expr;
238 char *ptr_name;
239 struct symbol *ptr_sym;
240 int arg_num = PTR_INT(data);
242 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
243 ptr_name = get_variable_from_expr(ptr_expr, &ptr_sym);
244 if (!ptr_name)
245 return;
246 set_state(my_id, ptr_name, ptr_sym, &isfree);
247 add_modification_hook(my_id, ptr_name, &set_unfree, NULL);
248 free_string(ptr_name);
251 static int possibly_allocated(struct state_list *slist)
253 struct sm_state *tmp;
255 FOR_EACH_PTR(slist, tmp) {
256 if (tmp->state == &allocated)
257 return 1;
258 if (tmp->state == &malloced)
259 return 1;
260 } END_FOR_EACH_PTR(tmp);
261 return 0;
264 static void check_sm_is_leaked(struct sm_state *sm)
266 if (possibly_allocated(sm->possible) &&
267 !is_null(sm->name, sm->sym) &&
268 !is_argument(sm->sym) &&
269 !parent_is_assigned(sm->sym))
270 sm_msg("error: memory leak of '%s'", sm->name);
273 static void check_tracker_is_leaked(struct tracker *t)
275 struct sm_state *sm;
277 sm = get_sm_state(t->owner, t->name, t->sym);
278 if (sm)
279 check_sm_is_leaked(sm);
280 __free_tracker(t);
283 static void match_declarations(struct symbol *sym)
285 const char *name;
287 if ((get_base_type(sym))->type == SYM_ARRAY) {
288 return;
291 name = sym->ident->name;
293 if (sym->initializer) {
294 if (is_allocation(sym->initializer)) {
295 set_state(my_id, name, sym, &malloced);
296 add_scope_hook((scope_hook *)&check_tracker_is_leaked,
297 alloc_tracker(my_id, name, sym));
298 scoped_state(my_id, name, sym);
299 } else {
300 assign_parent(sym);
305 static void check_for_allocated(void)
307 struct state_list *slist;
308 struct sm_state *tmp;
310 slist = get_all_states(my_id);
311 FOR_EACH_PTR(slist, tmp) {
312 check_sm_is_leaked(tmp);
313 } END_FOR_EACH_PTR(tmp);
314 free_slist(&slist);
317 static void match_return(struct expression *ret_value)
319 char *name;
320 struct symbol *sym;
322 name = get_variable_from_expr_complex(ret_value, &sym);
323 if (sym)
324 assign_parent(sym);
325 free_string(name);
326 check_for_allocated();
329 static void set_new_true_false_paths(const char *name, struct symbol *sym)
331 struct smatch_state *tmp;
333 tmp = get_state(my_id, name, sym);
335 if (!tmp) {
336 return;
339 if (tmp == &isfree) {
340 sm_msg("warn: why do you care about freed memory? '%s'", name);
343 if (tmp == &assigned) {
344 /* we don't care about assigned pointers any more */
345 return;
347 set_true_false_states(my_id, name, sym, &allocated, &isnull);
350 static void match_condition(struct expression *expr)
352 struct symbol *sym;
353 char *name;
355 expr = strip_expr(expr);
356 switch (expr->type) {
357 case EXPR_PREOP:
358 case EXPR_SYMBOL:
359 case EXPR_DEREF:
360 name = get_variable_from_expr(expr, &sym);
361 if (!name)
362 return;
363 set_new_true_false_paths(name, sym);
364 free_string(name);
365 return;
366 case EXPR_ASSIGNMENT:
367 /* You have to deal with stuff like if (a = b = c) */
368 match_condition(expr->right);
369 match_condition(expr->left);
370 return;
371 default:
372 return;
376 static void match_function_call(struct expression *expr)
378 struct expression *tmp;
379 struct symbol *sym;
380 char *name;
381 struct sm_state *state;
383 FOR_EACH_PTR(expr->args, tmp) {
384 tmp = strip_expr(tmp);
385 name = get_variable_from_expr_complex(tmp, &sym);
386 if (!name)
387 continue;
388 if ((state = get_sm_state(my_id, name, sym))) {
389 if (possibly_allocated(state->possible)) {
390 set_state(my_id, name, sym, &assigned);
393 assign_parent(sym);
394 free_string(name);
395 } END_FOR_EACH_PTR(tmp);
398 static void match_end_func(struct symbol *sym)
400 check_for_allocated();
401 free_trackers_and_list(&arguments);
404 static void register_funcs_from_file(void)
406 struct token *token;
407 const char *func;
408 int arg;
410 token = get_tokens_file("kernel.frees_argument");
411 if (!token)
412 return;
413 if (token_type(token) != TOKEN_STREAMBEGIN)
414 return;
415 token = token->next;
416 while (token_type(token) != TOKEN_STREAMEND) {
417 if (token_type(token) != TOKEN_IDENT)
418 return;
419 func = show_ident(token->ident);
420 token = token->next;
421 if (token_type(token) != TOKEN_NUMBER)
422 return;
423 arg = atoi(token->number);
424 add_function_hook(func, &match_free_func, INT_PTR(arg));
425 token = token->next;
427 clear_token_alloc();
430 void check_memory(int id)
432 my_id = id;
433 add_unmatched_state_hook(my_id, &unmatched_state);
434 add_hook(&match_function_def, FUNC_DEF_HOOK);
435 add_hook(&match_declarations, DECLARATION_HOOK);
436 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
437 add_hook(&match_condition, CONDITION_HOOK);
438 add_hook(&match_assign, ASSIGNMENT_HOOK);
439 add_hook(&match_return, RETURN_HOOK);
440 add_hook(&match_end_func, END_FUNC_HOOK);
441 if (option_project == PROJ_KERNEL) {
442 add_function_hook("kfree", &match_free_func, (void *)0);
443 register_funcs_from_file();
444 } else {
445 add_function_hook("free", &match_free_func, (void *)0);