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
, (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",
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(left_name
, my_id
, left_sym
, &allocated
);
96 free_string(left_name
);
99 static void match_free(const char *fn
, struct expression
*expr
, void *data
)
101 struct expression
*ptr_expr
;
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
);
110 if (!get_state(ptr_name
, my_id
, ptr_sym
))
112 set_state(ptr_name
, my_id
, ptr_sym
, &freed
);
113 free_string(ptr_name
);
116 static void check_slist(struct state_list
*slist
)
119 struct sm_state
*poss
;
121 FOR_EACH_PTR(slist
, sm
) {
122 if (sm
->owner
!= my_id
)
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
;
140 name
= get_variable_from_expr(expr
, &sym
);
143 get_implications(name
, sym
, '<', 0, <_zero
, &ge_zero
);
144 check_slist(lt_zero
);
146 free_slist(&ge_zero
);
147 free_slist(<_zero
);
151 static void match_return(struct statement
*stmt
)
155 ret_val
= get_value(stmt
->ret_value
);
156 if (ret_val
== UNDEFINED
) {
157 do_implication_check(stmt
->ret_value
);
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
))
169 set_true_false_states(name
, my_id
, sym
, &allocated
, &isnull
);
172 static void match_condition(struct expression
*expr
)
177 expr
= strip_expr(expr
);
182 name
= get_variable_from_expr(expr
, &sym
);
185 set_new_true_false_paths(name
, sym
);
188 case EXPR_ASSIGNMENT
:
189 match_condition(expr
->left
);
196 static void match_end_func(struct symbol
*sym
)
198 free_trackers_and_list(&arguments
);
201 static void register_free_funcs(void)
207 token
= get_tokens_file("kernel.frees_argument");
210 if (token_type(token
) != TOKEN_STREAMBEGIN
)
213 while (token_type(token
) != TOKEN_STREAMEND
) {
214 if (token_type(token
) != TOKEN_IDENT
)
216 func
= show_ident(token
->ident
);
218 if (token_type(token
) != TOKEN_NUMBER
)
220 arg
= atoi(token
->number
);
221 add_function_hook(func
, &match_free
, (void *)arg
);
227 static void register_put_funcs(void)
233 token
= get_tokens_file("kernel.puts_argument");
236 if (token_type(token
) != TOKEN_STREAMBEGIN
)
239 while (token_type(token
) != TOKEN_STREAMEND
) {
240 if (token_type(token
) != TOKEN_IDENT
)
242 func
= show_ident(token
->ident
);
244 if (token_type(token
) != TOKEN_NUMBER
)
246 arg
= atoi(token
->number
);
247 add_function_hook(func
, &match_free
, (void *)arg
);
253 static void register_allocation_funcs(void)
258 token
= get_tokens_file("kernel.allocation_funcs");
261 if (token_type(token
) != TOKEN_STREAMBEGIN
)
264 while (token_type(token
) != TOKEN_STREAMEND
) {
265 if (token_type(token
) != TOKEN_IDENT
)
267 func
= show_ident(token
->ident
);
268 add_function_assign_hook(func
, &match_allocation
, NULL
);
274 void check_leaks(int 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();