4 * Copyright (C) 2010 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
11 * The point of this check is to look for leaks.
12 * foo = malloc(); // <- mark it as allocated.
13 * A variable becomes &ok if we:
14 * 1) assign it to another variable.
15 * 2) pass it to a function.
17 * One complication is dealing with stuff like:
18 * foo->bar = malloc();
19 * foo->baz = malloc();
22 * The work around is that for now what this check only
23 * checks simple expressions and doesn't check whether
32 #include "smatch_slist.h"
39 static void set_parent(struct expression
*expr
, struct smatch_state
*state
);
41 static const char *allocation_funcs
[] = {
47 static char *alloc_parent_str(struct symbol
*sym
)
51 if (!sym
|| !sym
->ident
)
54 snprintf(buf
, 255, "%s", sym
->ident
->name
);
56 return alloc_string(buf
);
59 static char *get_parent_from_expr(struct expression
*expr
, struct symbol
**sym
)
63 expr
= strip_expr(expr
);
65 name
= get_variable_from_expr_complex(expr
, sym
);
67 if (!name
|| !*sym
|| !(*sym
)->ident
) {
71 return alloc_parent_str(*sym
);
74 static int is_local(struct expression
*expr
)
80 name
= get_variable_from_expr_complex(expr
, &sym
);
83 if (sym
->ctype
.modifiers
& (MOD_NONLOCAL
| MOD_STATIC
| MOD_ADDRESSABLE
))
91 static int is_param(struct expression
*expr
)
98 name
= get_variable_from_expr_complex(expr
, &sym
);
101 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, tmp
) {
106 } END_FOR_EACH_PTR(tmp
);
113 static void match_alloc(const char *fn
, struct expression
*expr
, void *unused
)
115 if (!is_local(expr
->left
))
117 if (is_param(expr
->left
))
119 if (expr
->left
->type
!= EXPR_SYMBOL
)
121 set_state_expr(my_id
, expr
->left
, &allocated
);
124 static void match_condition(struct expression
*expr
)
128 expr
= strip_expr(expr
);
130 switch (expr
->type
) {
134 sm
= get_sm_state_expr(my_id
, expr
);
135 if (sm
&& slist_has_state(sm
->possible
, &allocated
))
136 set_true_false_states_expr(my_id
, expr
, &allocated
, &ok
);
138 case EXPR_ASSIGNMENT
:
139 /* You have to deal with stuff like if (a = b = c) */
140 match_condition(expr
->left
);
147 static void set_parent(struct expression
*expr
, struct smatch_state
*state
)
152 name
= get_parent_from_expr(expr
, &sym
);
155 if (state
== &ok
&& !get_state(my_id
, name
, sym
))
157 set_state(my_id
, name
, sym
, state
);
162 static void match_function_call(struct expression
*expr
)
164 struct expression
*tmp
;
166 FOR_EACH_PTR(expr
->args
, tmp
) {
167 set_parent(tmp
, &ok
);
168 } END_FOR_EACH_PTR(tmp
);
171 static void warn_if_allocated(struct expression
*expr
)
176 sm
= get_sm_state_expr(my_id
, expr
);
179 if (!slist_has_state(sm
->possible
, &allocated
))
182 name
= get_variable_from_expr(expr
, NULL
);
183 sm_msg("warn: overwrite may leak '%s'", name
);
186 /* silence further warnings */
187 set_state_expr(my_id
, expr
, &ok
);
190 static void match_assign(struct expression
*expr
)
192 struct expression
*right
;
196 while (right
->type
== EXPR_ASSIGNMENT
)
199 warn_if_allocated(expr
->left
);
200 set_parent(right
, &ok
);
203 static void check_for_allocated(void)
205 struct state_list
*slist
;
206 struct sm_state
*tmp
;
208 slist
= get_all_states(my_id
);
209 FOR_EACH_PTR(slist
, tmp
) {
210 if (!slist_has_state(tmp
->possible
, &allocated
))
212 sm_msg("warn: possible memory leak of '%s'", tmp
->name
);
213 } END_FOR_EACH_PTR(tmp
);
217 static void match_return(struct expression
*ret_value
)
219 set_parent(ret_value
, &ok
);
220 check_for_allocated();
223 static void match_end_func(struct symbol
*sym
)
225 check_for_allocated();
228 void check_leaks(int id
)
234 for (i
= 0; i
< ARRAY_SIZE(allocation_funcs
); i
++)
235 add_function_assign_hook(allocation_funcs
[i
], &match_alloc
, NULL
);
237 add_hook(&match_condition
, CONDITION_HOOK
);
239 add_hook(&match_function_call
, FUNCTION_CALL_HOOK
);
240 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
242 add_hook(&match_return
, RETURN_HOOK
);
243 add_hook(&match_end_func
, END_FUNC_HOOK
);