Implied ranges. Part #2. Filters
[smatch.git] / check_leaks.c
blobe90b4c7536e1ca13b1b9c6d143df8a9673c0fa82
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_funcs_from_file(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_allocation_funcs(void)
229 struct token *token;
230 const char *func;
232 token = get_tokens_file("kernel.allocation_funcs");
233 if (!token)
234 return;
235 if (token_type(token) != TOKEN_STREAMBEGIN)
236 return;
237 token = token->next;
238 while (token_type(token) != TOKEN_STREAMEND) {
239 if (token_type(token) != TOKEN_IDENT)
240 return;
241 func = show_ident(token->ident);
242 add_function_assign_hook(func, &match_allocation, NULL);
243 token = token->next;
245 clear_token_alloc();
248 void check_leaks(int id)
250 int i;
252 my_id = id;
253 add_hook(&match_function_def, FUNC_DEF_HOOK);
254 add_hook(&match_condition, CONDITION_HOOK);
255 add_hook(&match_return, RETURN_HOOK);
256 add_hook(&match_end_func, END_FUNC_HOOK);
257 add_function_hook("kfree", &match_free, (void *)0);
258 register_funcs_from_file();
259 for(i = 0; allocation_funcs[i]; i++) {
260 add_function_assign_hook(allocation_funcs[i],
261 &match_allocation, NULL);
263 register_allocation_funcs();