2 * sparse/check_memory.c
4 * Copyright (C) 2008 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
14 #include "smatch_slist.h"
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
[] = {
41 static int is_argument(struct symbol
*sym
)
45 FOR_EACH_PTR(arguments
, arg
) {
48 } END_FOR_EACH_PTR(arg
);
52 static void match_function_def(struct symbol
*sym
)
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",
68 static void check_for_allocated(void)
70 struct state_list
*slist
;
73 slist
= get_all_states(my_id
);
74 FOR_EACH_PTR(slist
, tmp
) {
76 } END_FOR_EACH_PTR(tmp
);
80 static void match_allocation(const char *fn
, struct expression
*expr
,
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
)
89 if (is_argument(left_sym
))
91 if (left_sym
->ctype
.modifiers
&
92 (MOD_NONLOCAL
| MOD_STATIC
| MOD_ADDRESSABLE
))
94 set_state_expr(my_id
, expr
->left
, &allocated
);
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
))
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
)
114 struct sm_state
*poss
;
116 FOR_EACH_PTR(slist
, sm
) {
117 if (sm
->owner
!= my_id
)
119 if (sm
->sym
== skip_sym
&& !strcmp(sm
->name
, skip_name
))
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
;
138 name
= get_variable_from_expr(expr
, &sym
);
141 get_implications(name
, sym
, '<', 0, <_zero
, &ge_zero
);
142 check_slist(lt_zero
, skip_name
, skip_sym
);
144 free_slist(&ge_zero
);
145 free_slist(<_zero
);
149 static void match_return(struct expression
*ret_value
)
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
);
162 check_for_allocated();
165 static void set_old_true_false_paths(struct expression
*expr
)
167 if (!get_state_expr(my_id
, expr
))
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
) {
179 set_old_true_false_paths(expr
);
181 case EXPR_ASSIGNMENT
:
182 match_condition(expr
->left
);
189 static void match_end_func(struct symbol
*sym
)
191 free_trackers_and_list(&arguments
);
194 static void register_free_funcs(void)
200 token
= get_tokens_file("kernel.frees_argument");
203 if (token_type(token
) != TOKEN_STREAMBEGIN
)
206 while (token_type(token
) != TOKEN_STREAMEND
) {
207 if (token_type(token
) != TOKEN_IDENT
)
209 func
= show_ident(token
->ident
);
211 if (token_type(token
) != TOKEN_NUMBER
)
213 arg
= atoi(token
->number
);
214 add_function_hook(func
, &match_free
, INT_PTR(arg
));
220 static void register_put_funcs(void)
226 token
= get_tokens_file("kernel.puts_argument");
229 if (token_type(token
) != TOKEN_STREAMBEGIN
)
232 while (token_type(token
) != TOKEN_STREAMEND
) {
233 if (token_type(token
) != TOKEN_IDENT
)
235 func
= show_ident(token
->ident
);
237 if (token_type(token
) != TOKEN_NUMBER
)
239 arg
= atoi(token
->number
);
240 add_function_hook(func
, &match_free
, INT_PTR(arg
));
246 static void register_allocation_funcs(void)
251 token
= get_tokens_file("kernel.allocation_funcs");
254 if (token_type(token
) != TOKEN_STREAMBEGIN
)
257 while (token_type(token
) != TOKEN_STREAMEND
) {
258 if (token_type(token
) != TOKEN_IDENT
)
260 func
= show_ident(token
->ident
);
261 add_function_assign_hook(func
, &match_allocation
, NULL
);
267 void check_leaks(int 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);
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();