__module_put_and_exit() doesn't return.
[smatch.git] / check_leaks.c
bloba419d0505bd1e9b953ca2817d8a7b3106a8ceea5
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",
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(left_name, my_id, left_sym, &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 char *ptr_name;
103 struct symbol *ptr_sym;
104 int arg_num = (int)data;
106 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
107 ptr_name = get_variable_from_expr(ptr_expr, &ptr_sym);
108 if (!ptr_name)
109 return;
110 if (!get_state(ptr_name, my_id, ptr_sym))
111 return;
112 set_state(ptr_name, my_id, ptr_sym, &freed);
113 free_string(ptr_name);
116 static void check_slist(struct state_list *slist)
118 struct sm_state *sm;
119 struct sm_state *poss;
121 FOR_EACH_PTR(slist, sm) {
122 if (sm->owner != my_id)
123 continue;
124 FOR_EACH_PTR(sm->possible, poss) {
125 if (poss->state == &allocated)
126 smatch_msg("warn: '%s' possibly leaked on error"
127 " path (implied from line %d)",
128 sm->name, poss->line);
129 } END_FOR_EACH_PTR(poss);
130 } END_FOR_EACH_PTR(sm);
133 static void do_implication_check(struct expression *expr)
135 struct state_list *lt_zero = NULL;
136 struct state_list *ge_zero = NULL;
137 char *name;
138 struct symbol *sym;
140 name = get_variable_from_expr(expr, &sym);
141 if (!name || !sym)
142 goto free;
143 get_implications(name, sym, '<', 0, &lt_zero, &ge_zero);
144 check_slist(lt_zero);
145 free:
146 free_slist(&ge_zero);
147 free_slist(&lt_zero);
148 free_string(name);
151 static void match_return(struct statement *stmt)
153 int ret_val;
155 ret_val = get_value(stmt->ret_value);
156 if (ret_val == UNDEFINED) {
157 do_implication_check(stmt->ret_value);
158 return;
160 if (ret_val >= 0)
161 return;
162 check_for_allocated();
165 static void set_new_true_false_paths(const char *name, struct symbol *sym)
167 if (!get_state(name, my_id, sym))
168 return;
169 set_true_false_states(name, my_id, sym, &allocated, &isnull);
172 static void match_condition(struct expression *expr)
174 struct symbol *sym;
175 char *name;
177 expr = strip_expr(expr);
178 switch(expr->type) {
179 case EXPR_PREOP:
180 case EXPR_SYMBOL:
181 case EXPR_DEREF:
182 name = get_variable_from_expr(expr, &sym);
183 if (!name)
184 return;
185 set_new_true_false_paths(name, sym);
186 free_string(name);
187 return;
188 case EXPR_ASSIGNMENT:
189 match_condition(expr->left);
190 return;
191 default:
192 return;
196 static void match_end_func(struct symbol *sym)
198 free_trackers_and_list(&arguments);
201 static void register_free_funcs(void)
203 struct token *token;
204 const char *func;
205 int arg;
207 token = get_tokens_file("kernel.frees_argument");
208 if (!token)
209 return;
210 if (token_type(token) != TOKEN_STREAMBEGIN)
211 return;
212 token = token->next;
213 while (token_type(token) != TOKEN_STREAMEND) {
214 if (token_type(token) != TOKEN_IDENT)
215 return;
216 func = show_ident(token->ident);
217 token = token->next;
218 if (token_type(token) != TOKEN_NUMBER)
219 return;
220 arg = atoi(token->number);
221 add_function_hook(func, &match_free, (void *)arg);
222 token = token->next;
224 clear_token_alloc();
227 static void register_put_funcs(void)
229 struct token *token;
230 const char *func;
231 int arg;
233 token = get_tokens_file("kernel.puts_argument");
234 if (!token)
235 return;
236 if (token_type(token) != TOKEN_STREAMBEGIN)
237 return;
238 token = token->next;
239 while (token_type(token) != TOKEN_STREAMEND) {
240 if (token_type(token) != TOKEN_IDENT)
241 return;
242 func = show_ident(token->ident);
243 token = token->next;
244 if (token_type(token) != TOKEN_NUMBER)
245 return;
246 arg = atoi(token->number);
247 add_function_hook(func, &match_free, (void *)arg);
248 token = token->next;
250 clear_token_alloc();
253 static void register_allocation_funcs(void)
255 struct token *token;
256 const char *func;
258 token = get_tokens_file("kernel.allocation_funcs");
259 if (!token)
260 return;
261 if (token_type(token) != TOKEN_STREAMBEGIN)
262 return;
263 token = token->next;
264 while (token_type(token) != TOKEN_STREAMEND) {
265 if (token_type(token) != TOKEN_IDENT)
266 return;
267 func = show_ident(token->ident);
268 add_function_assign_hook(func, &match_allocation, NULL);
269 token = token->next;
271 clear_token_alloc();
274 void check_leaks(int id)
276 int i;
278 my_id = id;
279 add_hook(&match_function_def, FUNC_DEF_HOOK);
280 add_hook(&match_condition, CONDITION_HOOK);
281 add_hook(&match_return, RETURN_HOOK);
282 add_hook(&match_end_func, END_FUNC_HOOK);
283 add_function_hook("kfree", &match_free, (void *)0);
284 register_free_funcs();
285 register_put_funcs();
286 for(i = 0; allocation_funcs[i]; i++) {
287 add_function_assign_hook(allocation_funcs[i],
288 &match_allocation, NULL);
290 register_allocation_funcs();