Save function lists in smatch_data/
[smatch.git] / check_leaks.c
blob5854e308679ca713aeaa6b09ba91438ed921e923
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 int my_id;
18 STATE(allocated);
19 STATE(isnull);
20 STATE(freed);
23 The previous memory leak script found about 3 leaks. Almost all the leaks
24 it found were real though so that has to be said in it's favour.
26 The goal of this check is to find a lot more.
28 On this check, the plan is to look at return values. If the value is
29 negative we print an error for every local variable that is not freed.
32 static struct tracker_list *arguments;
34 static const char *allocation_funcs[] = {
35 "malloc",
36 "kmalloc",
37 "kzalloc",
38 NULL,
41 static int is_argument(struct symbol *sym)
43 struct tracker *arg;
45 FOR_EACH_PTR(arguments, arg) {
46 if (arg->sym == sym)
47 return 1;
48 } END_FOR_EACH_PTR(arg);
49 return 0;
52 static void match_function_def(struct symbol *sym)
54 struct symbol *arg;
56 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
57 add_tracker(&arguments, (arg->ident?arg->ident->name:"NULL"), my_id, arg);
58 } END_FOR_EACH_PTR(arg);
61 static void check_allocated(struct sm_state *sm)
63 if (slist_has_state(sm->possible, &allocated))
64 smatch_msg("warn: %s possibly leaked on error path", sm->name);
67 static void check_for_allocated(void)
69 struct state_list *slist;
70 struct sm_state *tmp;
72 slist = get_all_states(my_id);
73 FOR_EACH_PTR(slist, tmp) {
74 check_allocated(tmp);
75 } END_FOR_EACH_PTR(tmp);
76 free_slist(&slist);
79 static void match_allocation(const char *fn, struct expression *expr,
80 void *info)
82 char *left_name = NULL;
83 struct symbol *left_sym;
85 left_name = get_variable_from_expr(expr->left, &left_sym);
86 if (!left_name || !left_sym)
87 goto free;
88 if (is_argument(left_sym))
89 goto free;
90 if (left_sym->ctype.modifiers &
91 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
92 goto free;
93 set_state(left_name, my_id, left_sym, &allocated);
94 free:
95 free_string(left_name);
98 static void match_free(const char *fn, struct expression *expr, void *data)
100 struct expression *ptr_expr;
101 char *ptr_name;
102 struct symbol *ptr_sym;
103 int arg_num = (int)data;
105 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
106 ptr_name = get_variable_from_expr(ptr_expr, &ptr_sym);
107 if (!ptr_name)
108 return;
109 if (!get_state(ptr_name, my_id, ptr_sym))
110 return;
111 set_state(ptr_name, my_id, ptr_sym, &freed);
112 free_string(ptr_name);
115 static void check_slist(struct state_list *slist)
117 struct sm_state *sm;
119 FOR_EACH_PTR(slist, sm) {
120 if (sm->owner != my_id)
121 continue;
122 if (slist_has_state(sm->possible, &allocated))
123 smatch_msg("warn: %s possibly leaked on error path (implied)",
124 sm->name);
125 } END_FOR_EACH_PTR(sm);
128 static void do_implication_check(struct expression *expr)
130 struct state_list *lt_zero = NULL;
131 struct state_list *ge_zero = NULL;
132 char *name;
133 struct symbol *sym;
135 name = get_variable_from_expr(expr, &sym);
136 if (!name || !sym)
137 goto free;
138 get_implications(name, sym, '<', 0, &lt_zero, &ge_zero);
139 check_slist(lt_zero);
140 free:
141 free_slist(&ge_zero);
142 free_slist(&lt_zero);
143 free_string(name);
146 static void match_return(struct statement *stmt)
148 int ret_val;
150 ret_val = get_value(stmt->ret_value);
151 if (ret_val == UNDEFINED) {
152 do_implication_check(stmt->ret_value);
153 return;
155 if (ret_val >= 0)
156 return;
157 check_for_allocated();
160 static void set_new_true_false_paths(const char *name, struct symbol *sym)
162 if (!get_state(name, my_id, sym))
163 return;
164 set_true_false_states(name, my_id, sym, &allocated, &isnull);
167 static void match_condition(struct expression *expr)
169 struct symbol *sym;
170 char *name;
172 expr = strip_expr(expr);
173 switch(expr->type) {
174 case EXPR_PREOP:
175 case EXPR_SYMBOL:
176 case EXPR_DEREF:
177 name = get_variable_from_expr(expr, &sym);
178 if (!name)
179 return;
180 set_new_true_false_paths(name, sym);
181 free_string(name);
182 return;
183 case EXPR_ASSIGNMENT:
184 match_condition(expr->left);
185 return;
186 default:
187 return;
191 static void match_end_func(struct symbol *sym)
193 free_trackers_and_list(&arguments);
196 static void register_funcs_from_file(void)
198 const char *filename = "frees";
199 int fd;
200 struct token *token;
201 const char *func;
202 int arg;
204 fd = open(filename, O_RDONLY);
205 if (fd < 0)
206 return;
207 token = tokenize(filename, fd, NULL, NULL);
208 close(fd);
209 if (token_type(token) != TOKEN_STREAMBEGIN)
210 return;
211 token = token->next;
212 while (token_type(token) != TOKEN_STREAMEND) {
213 if (token_type(token) != TOKEN_IDENT)
214 return;
215 func = show_ident(token->ident);
216 token = token->next;
217 if (token_type(token) != TOKEN_NUMBER)
218 return;
219 arg = atoi(token->number);
220 add_function_hook(func, &match_free, (void *)arg);
221 token = token->next;
223 clear_token_alloc();
226 void check_leaks(int id)
228 int i;
230 my_id = id;
231 add_hook(&match_function_def, FUNC_DEF_HOOK);
232 add_hook(&match_condition, CONDITION_HOOK);
233 add_hook(&match_return, RETURN_HOOK);
234 add_hook(&match_end_func, END_FUNC_HOOK);
235 add_function_hook("kfree", &match_free, (void *)0);
236 register_funcs_from_file();
237 for(i = 0; allocation_funcs[i]; i++) {
238 add_function_assign_hook(allocation_funcs[i],
239 &match_allocation, NULL);