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 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
);
163 check_for_allocated();
166 static void set_old_true_false_paths(struct expression
*expr
)
168 if (!get_state_expr(my_id
, expr
))
170 set_true_false_states_expr(my_id
, expr
, &allocated
, &isnull
);
173 static void match_condition(struct expression
*expr
)
175 expr
= strip_expr(expr
);
180 set_old_true_false_paths(expr
);
182 case EXPR_ASSIGNMENT
:
183 match_condition(expr
->left
);
190 static void match_end_func(struct symbol
*sym
)
192 free_trackers_and_list(&arguments
);
195 static void register_free_funcs(void)
201 token
= get_tokens_file("kernel.frees_argument");
204 if (token_type(token
) != TOKEN_STREAMBEGIN
)
207 while (token_type(token
) != TOKEN_STREAMEND
) {
208 if (token_type(token
) != TOKEN_IDENT
)
210 func
= show_ident(token
->ident
);
212 if (token_type(token
) != TOKEN_NUMBER
)
214 arg
= atoi(token
->number
);
215 add_function_hook(func
, &match_free
, INT_PTR(arg
));
221 static void register_put_funcs(void)
227 token
= get_tokens_file("kernel.puts_argument");
230 if (token_type(token
) != TOKEN_STREAMBEGIN
)
233 while (token_type(token
) != TOKEN_STREAMEND
) {
234 if (token_type(token
) != TOKEN_IDENT
)
236 func
= show_ident(token
->ident
);
238 if (token_type(token
) != TOKEN_NUMBER
)
240 arg
= atoi(token
->number
);
241 add_function_hook(func
, &match_free
, INT_PTR(arg
));
247 static void register_allocation_funcs(void)
252 token
= get_tokens_file("kernel.allocation_funcs");
255 if (token_type(token
) != TOKEN_STREAMBEGIN
)
258 while (token_type(token
) != TOKEN_STREAMEND
) {
259 if (token_type(token
) != TOKEN_IDENT
)
261 func
= show_ident(token
->ident
);
262 add_function_assign_hook(func
, &match_allocation
, NULL
);
268 void check_leaks(int 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();