small clean up.
[smatch.git] / check_leaks.c
blob6119366a61d7e08f82eb729361a1d62ec36090c2
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, my_id, (arg->ident?arg->ident->name:"NULL"), 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 sm_msg("warn: '%s' possibly leaked on error path",
65 sm->name);
68 static void check_for_allocated(void)
70 struct state_list *slist;
71 struct sm_state *tmp;
73 slist = get_all_states(my_id);
74 FOR_EACH_PTR(slist, tmp) {
75 check_allocated(tmp);
76 } END_FOR_EACH_PTR(tmp);
77 free_slist(&slist);
80 static void match_allocation(const char *fn, struct expression *expr,
81 void *info)
83 char *left_name = NULL;
84 struct symbol *left_sym;
86 left_name = get_variable_from_expr(expr->left, &left_sym);
87 if (!left_name || !left_sym)
88 goto free;
89 if (is_argument(left_sym))
90 goto free;
91 if (left_sym->ctype.modifiers &
92 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
93 goto free;
94 set_state_expr(my_id, expr->left, &allocated);
95 free:
96 free_string(left_name);
99 static void match_free(const char *fn, struct expression *expr, void *data)
101 struct expression *ptr_expr;
102 long arg_num = PTR_INT(data);
104 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
105 if (!get_state_expr(my_id, ptr_expr))
106 return;
107 set_state_expr(my_id, ptr_expr, &freed);
110 static void check_slist(struct state_list *slist)
112 struct sm_state *sm;
113 struct sm_state *poss;
115 FOR_EACH_PTR(slist, sm) {
116 if (sm->owner != my_id)
117 continue;
118 FOR_EACH_PTR(sm->possible, poss) {
119 if (poss->state == &allocated)
120 sm_msg("warn: '%s' possibly leaked on error"
121 " path (implied from line %d)",
122 sm->name, poss->line);
123 } END_FOR_EACH_PTR(poss);
124 } END_FOR_EACH_PTR(sm);
127 static void do_implication_check(struct expression *expr)
129 struct state_list *lt_zero = NULL;
130 struct state_list *ge_zero = NULL;
131 char *name;
132 struct symbol *sym;
134 name = get_variable_from_expr(expr, &sym);
135 if (!name || !sym)
136 goto free;
137 get_implications(name, sym, '<', 0, &lt_zero, &ge_zero);
138 check_slist(lt_zero);
139 free:
140 free_slist(&ge_zero);
141 free_slist(&lt_zero);
142 free_string(name);
145 static void match_return(struct statement *stmt)
147 int ret_val;
149 ret_val = get_value(stmt->ret_value);
150 if (ret_val == UNDEFINED) {
151 do_implication_check(stmt->ret_value);
152 return;
154 if (ret_val >= 0)
155 return;
156 check_for_allocated();
159 static void set_old_true_false_paths(struct expression *expr)
161 if (!get_state_expr(my_id, expr))
162 return;
163 set_true_false_states_expr(my_id, expr, &allocated, &isnull);
166 static void match_condition(struct expression *expr)
168 expr = strip_expr(expr);
169 switch(expr->type) {
170 case EXPR_PREOP:
171 case EXPR_SYMBOL:
172 case EXPR_DEREF:
173 set_old_true_false_paths(expr);
174 return;
175 case EXPR_ASSIGNMENT:
176 match_condition(expr->left);
177 return;
178 default:
179 return;
183 static void match_end_func(struct symbol *sym)
185 free_trackers_and_list(&arguments);
188 static void register_free_funcs(void)
190 struct token *token;
191 const char *func;
192 int arg;
194 token = get_tokens_file("kernel.frees_argument");
195 if (!token)
196 return;
197 if (token_type(token) != TOKEN_STREAMBEGIN)
198 return;
199 token = token->next;
200 while (token_type(token) != TOKEN_STREAMEND) {
201 if (token_type(token) != TOKEN_IDENT)
202 return;
203 func = show_ident(token->ident);
204 token = token->next;
205 if (token_type(token) != TOKEN_NUMBER)
206 return;
207 arg = atoi(token->number);
208 add_function_hook(func, &match_free, INT_PTR(arg));
209 token = token->next;
211 clear_token_alloc();
214 static void register_put_funcs(void)
216 struct token *token;
217 const char *func;
218 int arg;
220 token = get_tokens_file("kernel.puts_argument");
221 if (!token)
222 return;
223 if (token_type(token) != TOKEN_STREAMBEGIN)
224 return;
225 token = token->next;
226 while (token_type(token) != TOKEN_STREAMEND) {
227 if (token_type(token) != TOKEN_IDENT)
228 return;
229 func = show_ident(token->ident);
230 token = token->next;
231 if (token_type(token) != TOKEN_NUMBER)
232 return;
233 arg = atoi(token->number);
234 add_function_hook(func, &match_free, INT_PTR(arg));
235 token = token->next;
237 clear_token_alloc();
240 static void register_allocation_funcs(void)
242 struct token *token;
243 const char *func;
245 token = get_tokens_file("kernel.allocation_funcs");
246 if (!token)
247 return;
248 if (token_type(token) != TOKEN_STREAMBEGIN)
249 return;
250 token = token->next;
251 while (token_type(token) != TOKEN_STREAMEND) {
252 if (token_type(token) != TOKEN_IDENT)
253 return;
254 func = show_ident(token->ident);
255 add_function_assign_hook(func, &match_allocation, NULL);
256 token = token->next;
258 clear_token_alloc();
261 void check_leaks(int id)
263 int i;
265 if (!option_spammy)
266 return;
268 my_id = id;
269 set_default_state(my_id, &undefined);
270 add_hook(&match_function_def, FUNC_DEF_HOOK);
271 add_hook(&match_condition, CONDITION_HOOK);
272 add_hook(&match_return, RETURN_HOOK);
273 add_hook(&match_end_func, END_FUNC_HOOK);
274 add_function_hook("kfree", &match_free, (void *)0);
275 register_free_funcs();
276 register_put_funcs();
277 for(i = 0; allocation_funcs[i]; i++) {
278 add_function_assign_hook(allocation_funcs[i],
279 &match_allocation, NULL);
281 register_allocation_funcs();