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
)
113 struct sm_state
*poss
;
115 FOR_EACH_PTR(slist
, sm
) {
116 if (sm
->owner
!= my_id
)
118 FOR_EACH_PTR(sm
->possible
, poss
) {
119 if (poss
->state
== &allocated
)
120 sm_msg("warn: '%s' possibly leaked on error"
121 " path (implied from line %d)",
122 sm
->name
, poss
->line
);
123 } END_FOR_EACH_PTR(poss
);
124 } END_FOR_EACH_PTR(sm
);
127 static void do_implication_check(struct expression
*expr
)
129 struct state_list
*lt_zero
= NULL
;
130 struct state_list
*ge_zero
= NULL
;
134 name
= get_variable_from_expr(expr
, &sym
);
137 get_implications(name
, sym
, '<', 0, <_zero
, &ge_zero
);
138 check_slist(lt_zero
);
140 free_slist(&ge_zero
);
141 free_slist(<_zero
);
145 static void match_return(struct statement
*stmt
)
149 ret_val
= get_value(stmt
->ret_value
);
150 if (ret_val
== UNDEFINED
) {
151 do_implication_check(stmt
->ret_value
);
156 check_for_allocated();
159 static void set_old_true_false_paths(struct expression
*expr
)
161 if (!get_state_expr(my_id
, expr
))
163 set_true_false_states_expr(my_id
, expr
, &allocated
, &isnull
);
166 static void match_condition(struct expression
*expr
)
168 expr
= strip_expr(expr
);
173 set_old_true_false_paths(expr
);
175 case EXPR_ASSIGNMENT
:
176 match_condition(expr
->left
);
183 static void match_end_func(struct symbol
*sym
)
185 free_trackers_and_list(&arguments
);
188 static void register_free_funcs(void)
194 token
= get_tokens_file("kernel.frees_argument");
197 if (token_type(token
) != TOKEN_STREAMBEGIN
)
200 while (token_type(token
) != TOKEN_STREAMEND
) {
201 if (token_type(token
) != TOKEN_IDENT
)
203 func
= show_ident(token
->ident
);
205 if (token_type(token
) != TOKEN_NUMBER
)
207 arg
= atoi(token
->number
);
208 add_function_hook(func
, &match_free
, INT_PTR(arg
));
214 static void register_put_funcs(void)
220 token
= get_tokens_file("kernel.puts_argument");
223 if (token_type(token
) != TOKEN_STREAMBEGIN
)
226 while (token_type(token
) != TOKEN_STREAMEND
) {
227 if (token_type(token
) != TOKEN_IDENT
)
229 func
= show_ident(token
->ident
);
231 if (token_type(token
) != TOKEN_NUMBER
)
233 arg
= atoi(token
->number
);
234 add_function_hook(func
, &match_free
, INT_PTR(arg
));
240 static void register_allocation_funcs(void)
245 token
= get_tokens_file("kernel.allocation_funcs");
248 if (token_type(token
) != TOKEN_STREAMBEGIN
)
251 while (token_type(token
) != TOKEN_STREAMEND
) {
252 if (token_type(token
) != TOKEN_IDENT
)
254 func
= show_ident(token
->ident
);
255 add_function_assign_hook(func
, &match_allocation
, NULL
);
261 void check_leaks(int id
)
269 set_default_state(my_id
, &undefined
);
270 add_hook(&match_function_def
, FUNC_DEF_HOOK
);
271 add_hook(&match_condition
, CONDITION_HOOK
);
272 add_hook(&match_return
, RETURN_HOOK
);
273 add_hook(&match_end_func
, END_FUNC_HOOK
);
274 add_function_hook("kfree", &match_free
, (void *)0);
275 register_free_funcs();
276 register_put_funcs();
277 for(i
= 0; allocation_funcs
[i
]; i
++) {
278 add_function_assign_hook(allocation_funcs
[i
],
279 &match_allocation
, NULL
);
281 register_allocation_funcs();