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"
16 static void check_sm_is_leaked(struct sm_state
*sm
);
28 malloced --> allocated --> assigned --> isfree +
29 \-> isnull. \-> isfree +
35 static struct tracker_list
*arguments
;
37 static const char *allocation_funcs
[] = {
44 static char *get_parent_name(struct symbol
*sym
)
48 if (!sym
|| !sym
->ident
)
51 snprintf(buf
, 255, "-%s", sym
->ident
->name
);
53 return alloc_string(buf
);
56 static int is_parent_sym(const char *name
)
58 if (!strncmp(name
, "-", 1))
63 static int is_complex(struct expression
*expr
)
68 name
= get_variable_from_expr(expr
, NULL
);
75 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
77 if (is_parent_sym(sm
->name
))
82 static void assign_parent(struct symbol
*sym
)
86 name
= get_parent_name(sym
);
89 set_state(my_id
, name
, sym
, &assigned
);
93 static int parent_is_assigned(struct symbol
*sym
)
95 struct smatch_state
*state
;
98 name
= get_parent_name(sym
);
101 state
= get_state(my_id
, name
, sym
);
103 if (state
== &assigned
)
108 static int is_allocation(struct expression
*expr
)
113 if (expr
->type
!= EXPR_CALL
)
116 if (!(fn_name
= get_variable_from_expr(expr
->fn
, NULL
)))
119 for (i
= 0; allocation_funcs
[i
]; i
++) {
120 if (!strcmp(fn_name
, allocation_funcs
[i
])) {
121 free_string(fn_name
);
125 free_string(fn_name
);
129 static int is_freed(const char *name
, struct symbol
*sym
)
131 struct state_list
*slist
;
133 slist
= get_possible_states(my_id
, name
, sym
);
134 if (slist_has_state(slist
, &isfree
)) {
140 static int is_argument(struct symbol
*sym
)
144 FOR_EACH_PTR(arguments
, arg
) {
147 } END_FOR_EACH_PTR(arg
);
151 static void match_function_def(struct symbol
*sym
)
155 FOR_EACH_PTR(sym
->ctype
.base_type
->arguments
, arg
) {
156 add_tracker(&arguments
, my_id
, (arg
->ident
?arg
->ident
->name
:"NULL"), arg
);
157 } END_FOR_EACH_PTR(arg
);
160 static int is_parent(struct expression
*expr
)
162 if (expr
->type
== EXPR_DEREF
)
167 static void match_assign(struct expression
*expr
)
169 struct expression
*left
, *right
;
170 char *left_name
= NULL
;
171 char *right_name
= NULL
;
172 struct symbol
*left_sym
, *right_sym
;
173 struct smatch_state
*state
;
174 struct state_list
*slist
;
175 struct sm_state
*tmp
;
177 left
= strip_expr(expr
->left
);
178 left_name
= get_variable_from_expr_complex(left
, &left_sym
);
180 right
= strip_expr(expr
->right
);
181 while (right
->type
== EXPR_ASSIGNMENT
)
184 if (left_name
&& left_sym
&& is_allocation(right
) &&
185 !(left_sym
->ctype
.modifiers
&
186 (MOD_NONLOCAL
| MOD_STATIC
| MOD_ADDRESSABLE
)) &&
187 !parent_is_assigned(left_sym
)) {
188 set_state(my_id
, left_name
, left_sym
, &malloced
);
192 right_name
= get_variable_from_expr_complex(right
, &right_sym
);
194 if (right_name
&& (state
= get_state(my_id
, right_name
, right_sym
))) {
195 if (state
== &isfree
&& !is_complex(right
))
196 sm_msg("error: assigning freed pointer '%s'", right_name
);
197 set_state(my_id
, right_name
, right_sym
, &assigned
);
200 if (is_zero(expr
->right
)) {
201 slist
= get_possible_states(my_id
, left_name
, left_sym
);
203 FOR_EACH_PTR(slist
, tmp
) {
204 check_sm_is_leaked(tmp
);
205 } END_FOR_EACH_PTR(tmp
);
208 if (is_freed(left_name
, left_sym
)) {
209 set_state(my_id
, left_name
, left_sym
, &unfree
);
211 if (left_name
&& is_parent(left
))
212 assign_parent(left_sym
);
213 if (right_name
&& is_parent(right
))
214 assign_parent(right_sym
);
216 free_string(left_name
);
217 free_string(right_name
);
220 static int is_null(const char *name
, struct symbol
*sym
)
222 struct smatch_state
*state
;
224 state
= get_state(my_id
, name
, sym
);
225 if (state
&& !strcmp(state
->name
, "isnull"))
230 static void set_unfree(const char *name
, struct symbol
*sym
, struct expression
*expr
, void *unused
)
232 set_state(my_id
, name
, sym
, &unfree
);
235 static void match_free_func(const char *fn
, struct expression
*expr
, void *data
)
237 struct expression
*ptr_expr
;
239 struct symbol
*ptr_sym
;
240 int arg_num
= PTR_INT(data
);
242 ptr_expr
= get_argument_from_call_expr(expr
->args
, arg_num
);
243 ptr_name
= get_variable_from_expr(ptr_expr
, &ptr_sym
);
246 set_state(my_id
, ptr_name
, ptr_sym
, &isfree
);
247 add_modification_hook(my_id
, ptr_name
, &set_unfree
, NULL
);
248 free_string(ptr_name
);
251 static int possibly_allocated(struct state_list
*slist
)
253 struct sm_state
*tmp
;
255 FOR_EACH_PTR(slist
, tmp
) {
256 if (tmp
->state
== &allocated
)
258 if (tmp
->state
== &malloced
)
260 } END_FOR_EACH_PTR(tmp
);
264 static void check_sm_is_leaked(struct sm_state
*sm
)
266 if (possibly_allocated(sm
->possible
) &&
267 !is_null(sm
->name
, sm
->sym
) &&
268 !is_argument(sm
->sym
) &&
269 !parent_is_assigned(sm
->sym
))
270 sm_msg("error: memory leak of '%s'", sm
->name
);
273 static void check_tracker_is_leaked(struct tracker
*t
)
277 sm
= get_sm_state(t
->owner
, t
->name
, t
->sym
);
279 check_sm_is_leaked(sm
);
283 static void match_declarations(struct symbol
*sym
)
287 if ((get_base_type(sym
))->type
== SYM_ARRAY
) {
291 name
= sym
->ident
->name
;
293 if (sym
->initializer
) {
294 if (is_allocation(sym
->initializer
)) {
295 set_state(my_id
, name
, sym
, &malloced
);
296 add_scope_hook((scope_hook
*)&check_tracker_is_leaked
,
297 alloc_tracker(my_id
, name
, sym
));
298 scoped_state(my_id
, name
, sym
);
305 static void check_for_allocated(void)
307 struct state_list
*slist
;
308 struct sm_state
*tmp
;
310 slist
= get_all_states(my_id
);
311 FOR_EACH_PTR(slist
, tmp
) {
312 check_sm_is_leaked(tmp
);
313 } END_FOR_EACH_PTR(tmp
);
317 static void match_return(struct expression
*ret_value
)
322 name
= get_variable_from_expr_complex(ret_value
, &sym
);
326 check_for_allocated();
329 static void set_new_true_false_paths(const char *name
, struct symbol
*sym
)
331 struct smatch_state
*tmp
;
333 tmp
= get_state(my_id
, name
, sym
);
339 if (tmp
== &isfree
) {
340 sm_msg("warn: why do you care about freed memory? '%s'", name
);
343 if (tmp
== &assigned
) {
344 /* we don't care about assigned pointers any more */
347 set_true_false_states(my_id
, name
, sym
, &allocated
, &isnull
);
350 static void match_condition(struct expression
*expr
)
355 expr
= strip_expr(expr
);
356 switch (expr
->type
) {
360 name
= get_variable_from_expr(expr
, &sym
);
363 set_new_true_false_paths(name
, sym
);
366 case EXPR_ASSIGNMENT
:
367 /* You have to deal with stuff like if (a = b = c) */
368 match_condition(expr
->right
);
369 match_condition(expr
->left
);
376 static void match_function_call(struct expression
*expr
)
378 struct expression
*tmp
;
381 struct sm_state
*state
;
383 FOR_EACH_PTR(expr
->args
, tmp
) {
384 tmp
= strip_expr(tmp
);
385 name
= get_variable_from_expr_complex(tmp
, &sym
);
388 if ((state
= get_sm_state(my_id
, name
, sym
))) {
389 if (possibly_allocated(state
->possible
)) {
390 set_state(my_id
, name
, sym
, &assigned
);
395 } END_FOR_EACH_PTR(tmp
);
398 static void match_end_func(struct symbol
*sym
)
400 check_for_allocated();
401 free_trackers_and_list(&arguments
);
404 static void register_funcs_from_file(void)
410 token
= get_tokens_file("kernel.frees_argument");
413 if (token_type(token
) != TOKEN_STREAMBEGIN
)
416 while (token_type(token
) != TOKEN_STREAMEND
) {
417 if (token_type(token
) != TOKEN_IDENT
)
419 func
= show_ident(token
->ident
);
421 if (token_type(token
) != TOKEN_NUMBER
)
423 arg
= atoi(token
->number
);
424 add_function_hook(func
, &match_free_func
, INT_PTR(arg
));
430 void check_memory(int id
)
433 add_unmatched_state_hook(my_id
, &unmatched_state
);
434 add_hook(&match_function_def
, FUNC_DEF_HOOK
);
435 add_hook(&match_declarations
, DECLARATION_HOOK
);
436 add_hook(&match_function_call
, FUNCTION_CALL_HOOK
);
437 add_hook(&match_condition
, CONDITION_HOOK
);
438 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
439 add_hook(&match_return
, RETURN_HOOK
);
440 add_hook(&match_end_func
, END_FUNC_HOOK
);
441 if (option_project
== PROJ_KERNEL
) {
442 add_function_hook("kfree", &match_free_func
, (void *)0);
443 register_funcs_from_file();
445 add_function_hook("free", &match_free_func
, (void *)0);