Add a new (soon to be mandatory) --project= option
[smatch.git] / check_leaks.c
blobe781a7726d6dcd01a6907870b205e0b4115920e9
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, const char *skip_name,
111 struct symbol *skip_sym)
113 struct sm_state *sm;
114 struct sm_state *poss;
116 FOR_EACH_PTR(slist, sm) {
117 if (sm->owner != my_id)
118 continue;
119 if (sm->sym == skip_sym && !strcmp(sm->name, skip_name))
120 continue;
121 FOR_EACH_PTR(sm->possible, poss) {
122 if (poss->state == &allocated)
123 sm_msg("warn: '%s' possibly leaked on error"
124 " path (implied from line %d)",
125 sm->name, poss->line);
126 } END_FOR_EACH_PTR(poss);
127 } END_FOR_EACH_PTR(sm);
130 static void do_implication_check(struct expression *expr, const char *skip_name,
131 struct symbol *skip_sym)
133 struct state_list *lt_zero = NULL;
134 struct state_list *ge_zero = NULL;
135 char *name;
136 struct symbol *sym;
138 name = get_variable_from_expr(expr, &sym);
139 if (!name || !sym)
140 goto free;
141 get_implications(name, sym, '<', 0, &lt_zero, &ge_zero);
142 check_slist(lt_zero, skip_name, skip_sym);
143 free:
144 free_slist(&ge_zero);
145 free_slist(&lt_zero);
146 free_string(name);
149 static void match_return(struct expression *ret_value)
151 int ret_val;
152 char *skip_name;
153 struct symbol *skip_sym;
155 ret_val = get_value(ret_value);
156 skip_name = get_variable_from_expr(ret_value, &skip_sym);
157 if (ret_val == UNDEFINED) {
158 do_implication_check(ret_value, skip_name, skip_sym);
159 return;
161 if (ret_val >= 0)
162 return;
163 check_for_allocated();
166 static void set_old_true_false_paths(struct expression *expr)
168 if (!get_state_expr(my_id, expr))
169 return;
170 set_true_false_states_expr(my_id, expr, &allocated, &isnull);
173 static void match_condition(struct expression *expr)
175 expr = strip_expr(expr);
176 switch(expr->type) {
177 case EXPR_PREOP:
178 case EXPR_SYMBOL:
179 case EXPR_DEREF:
180 set_old_true_false_paths(expr);
181 return;
182 case EXPR_ASSIGNMENT:
183 match_condition(expr->left);
184 return;
185 default:
186 return;
190 static void match_end_func(struct symbol *sym)
192 free_trackers_and_list(&arguments);
195 static void register_free_funcs(void)
197 struct token *token;
198 const char *func;
199 int arg;
201 token = get_tokens_file("kernel.frees_argument");
202 if (!token)
203 return;
204 if (token_type(token) != TOKEN_STREAMBEGIN)
205 return;
206 token = token->next;
207 while (token_type(token) != TOKEN_STREAMEND) {
208 if (token_type(token) != TOKEN_IDENT)
209 return;
210 func = show_ident(token->ident);
211 token = token->next;
212 if (token_type(token) != TOKEN_NUMBER)
213 return;
214 arg = atoi(token->number);
215 add_function_hook(func, &match_free, INT_PTR(arg));
216 token = token->next;
218 clear_token_alloc();
221 static void register_put_funcs(void)
223 struct token *token;
224 const char *func;
225 int arg;
227 token = get_tokens_file("kernel.puts_argument");
228 if (!token)
229 return;
230 if (token_type(token) != TOKEN_STREAMBEGIN)
231 return;
232 token = token->next;
233 while (token_type(token) != TOKEN_STREAMEND) {
234 if (token_type(token) != TOKEN_IDENT)
235 return;
236 func = show_ident(token->ident);
237 token = token->next;
238 if (token_type(token) != TOKEN_NUMBER)
239 return;
240 arg = atoi(token->number);
241 add_function_hook(func, &match_free, INT_PTR(arg));
242 token = token->next;
244 clear_token_alloc();
247 static void register_allocation_funcs(void)
249 struct token *token;
250 const char *func;
252 token = get_tokens_file("kernel.allocation_funcs");
253 if (!token)
254 return;
255 if (token_type(token) != TOKEN_STREAMBEGIN)
256 return;
257 token = token->next;
258 while (token_type(token) != TOKEN_STREAMEND) {
259 if (token_type(token) != TOKEN_IDENT)
260 return;
261 func = show_ident(token->ident);
262 add_function_assign_hook(func, &match_allocation, NULL);
263 token = token->next;
265 clear_token_alloc();
268 void check_leaks(int id)
270 int i;
272 if (!option_spammy)
273 return;
275 my_id = id;
276 set_default_state(my_id, &undefined);
277 add_hook(&match_function_def, FUNC_DEF_HOOK);
278 add_hook(&match_condition, CONDITION_HOOK);
279 add_hook(&match_return, RETURN_HOOK);
280 add_hook(&match_end_func, END_FUNC_HOOK);
281 add_function_hook("kfree", &match_free, (void *)0);
282 register_free_funcs();
283 register_put_funcs();
284 for(i = 0; allocation_funcs[i]; i++) {
285 add_function_assign_hook(allocation_funcs[i],
286 &match_allocation, NULL);
288 register_allocation_funcs();