core: make fake cur_slists stackable
[smatch.git] / check_leaks.c
blob3539bd100a315a2e6cf64afab0d5f4be1e19a43d
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 long long ret_val;
152 char *skip_name;
153 struct symbol *skip_sym;
155 skip_name = get_variable_from_expr(ret_value, &skip_sym);
156 if (!get_value(ret_value, &ret_val)) {
157 do_implication_check(ret_value, skip_name, skip_sym);
158 return;
160 if (ret_val >= 0)
161 return;
162 check_for_allocated();
165 static void set_old_true_false_paths(struct expression *expr)
167 if (!get_state_expr(my_id, expr))
168 return;
169 set_true_false_states_expr(my_id, expr, &allocated, &isnull);
172 static void match_condition(struct expression *expr)
174 expr = strip_expr(expr);
175 switch (expr->type) {
176 case EXPR_PREOP:
177 case EXPR_SYMBOL:
178 case EXPR_DEREF:
179 set_old_true_false_paths(expr);
180 return;
181 case EXPR_ASSIGNMENT:
182 match_condition(expr->left);
183 return;
184 default:
185 return;
189 static void match_end_func(struct symbol *sym)
191 free_trackers_and_list(&arguments);
194 static void register_free_funcs(void)
196 struct token *token;
197 const char *func;
198 int arg;
200 token = get_tokens_file("kernel.frees_argument");
201 if (!token)
202 return;
203 if (token_type(token) != TOKEN_STREAMBEGIN)
204 return;
205 token = token->next;
206 while (token_type(token) != TOKEN_STREAMEND) {
207 if (token_type(token) != TOKEN_IDENT)
208 return;
209 func = show_ident(token->ident);
210 token = token->next;
211 if (token_type(token) != TOKEN_NUMBER)
212 return;
213 arg = atoi(token->number);
214 add_function_hook(func, &match_free, INT_PTR(arg));
215 token = token->next;
217 clear_token_alloc();
220 static void register_put_funcs(void)
222 struct token *token;
223 const char *func;
224 int arg;
226 token = get_tokens_file("kernel.puts_argument");
227 if (!token)
228 return;
229 if (token_type(token) != TOKEN_STREAMBEGIN)
230 return;
231 token = token->next;
232 while (token_type(token) != TOKEN_STREAMEND) {
233 if (token_type(token) != TOKEN_IDENT)
234 return;
235 func = show_ident(token->ident);
236 token = token->next;
237 if (token_type(token) != TOKEN_NUMBER)
238 return;
239 arg = atoi(token->number);
240 add_function_hook(func, &match_free, INT_PTR(arg));
241 token = token->next;
243 clear_token_alloc();
246 static void register_allocation_funcs(void)
248 struct token *token;
249 const char *func;
251 token = get_tokens_file("kernel.allocation_funcs");
252 if (!token)
253 return;
254 if (token_type(token) != TOKEN_STREAMBEGIN)
255 return;
256 token = token->next;
257 while (token_type(token) != TOKEN_STREAMEND) {
258 if (token_type(token) != TOKEN_IDENT)
259 return;
260 func = show_ident(token->ident);
261 add_function_assign_hook(func, &match_allocation, NULL);
262 token = token->next;
264 clear_token_alloc();
267 void check_leaks(int id)
269 int i;
271 if (!option_spammy)
272 return;
274 my_id = id;
275 add_hook(&match_function_def, FUNC_DEF_HOOK);
276 add_hook(&match_condition, CONDITION_HOOK);
277 add_hook(&match_return, RETURN_HOOK);
278 add_hook(&match_end_func, END_FUNC_HOOK);
279 if (option_project == PROJ_KERNEL)
280 add_function_hook("kfree", &match_free, (void *)0);
281 else
282 add_function_hook("free", &match_free, (void *)0);
283 register_free_funcs();
284 register_put_funcs();
285 for (i = 0; allocation_funcs[i]; i++) {
286 add_function_assign_hook(allocation_funcs[i],
287 &match_allocation, NULL);
289 register_allocation_funcs();