2 * Copyright (C) 2008 Dan Carpenter.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
22 #include "smatch_slist.h"
24 static void check_sm_is_leaked(struct sm_state
*sm
);
36 malloced --> allocated --> assigned --> isfree +
37 \-> isnull. \-> isfree +
43 static struct tracker_list
*arguments
;
45 static const char *allocation_funcs
[] = {
52 static char *get_parent_name(struct symbol
*sym
)
56 if (!sym
|| !sym
->ident
)
59 snprintf(buf
, 255, "-%s", sym
->ident
->name
);
61 return alloc_string(buf
);
64 static int is_parent_sym(const char *name
)
66 if (!strncmp(name
, "-", 1))
71 static int is_complex(struct expression
*expr
)
76 name
= expr_to_var(expr
);
83 static struct smatch_state
*unmatched_state(struct sm_state
*sm
)
85 if (is_parent_sym(sm
->name
))
90 static void assign_parent(struct symbol
*sym
)
94 name
= get_parent_name(sym
);
97 set_state(my_id
, name
, sym
, &assigned
);
101 static int parent_is_assigned(struct symbol
*sym
)
103 struct smatch_state
*state
;
106 name
= get_parent_name(sym
);
109 state
= get_state(my_id
, name
, sym
);
111 if (state
== &assigned
)
116 static int is_allocation(struct expression
*expr
)
121 if (expr
->type
!= EXPR_CALL
)
124 if (!(fn_name
= expr_to_var_sym(expr
->fn
, NULL
)))
127 for (i
= 0; allocation_funcs
[i
]; i
++) {
128 if (!strcmp(fn_name
, allocation_funcs
[i
])) {
129 free_string(fn_name
);
133 free_string(fn_name
);
137 static int is_freed(const char *name
, struct symbol
*sym
)
139 struct state_list
*slist
;
141 slist
= get_possible_states(my_id
, name
, sym
);
142 if (slist_has_state(slist
, &isfree
)) {
148 static int is_argument(struct symbol
*sym
)
152 FOR_EACH_PTR(arguments
, arg
) {
155 } END_FOR_EACH_PTR(arg
);
159 static void match_function_def(struct symbol
*sym
)
163 FOR_EACH_PTR(sym
->ctype
.base_type
->arguments
, arg
) {
164 add_tracker(&arguments
, my_id
, (arg
->ident
?arg
->ident
->name
:"NULL"), arg
);
165 } END_FOR_EACH_PTR(arg
);
168 static int is_parent(struct expression
*expr
)
170 if (expr
->type
== EXPR_DEREF
)
175 static void match_assign(struct expression
*expr
)
177 struct expression
*left
, *right
;
178 char *left_name
= NULL
;
179 char *right_name
= NULL
;
180 struct symbol
*left_sym
, *right_sym
;
181 struct smatch_state
*state
;
182 struct state_list
*slist
;
183 struct sm_state
*tmp
;
185 left
= strip_expr(expr
->left
);
186 left_name
= expr_to_str_sym(left
, &left_sym
);
188 right
= strip_expr(expr
->right
);
189 while (right
->type
== EXPR_ASSIGNMENT
)
192 if (left_name
&& left_sym
&& is_allocation(right
) &&
193 !(left_sym
->ctype
.modifiers
&
194 (MOD_NONLOCAL
| MOD_STATIC
| MOD_ADDRESSABLE
)) &&
195 !parent_is_assigned(left_sym
)) {
196 set_state(my_id
, left_name
, left_sym
, &malloced
);
200 right_name
= expr_to_str_sym(right
, &right_sym
);
202 if (right_name
&& (state
= get_state(my_id
, right_name
, right_sym
))) {
203 if (state
== &isfree
&& !is_complex(right
))
204 sm_msg("error: assigning freed pointer '%s'", right_name
);
205 set_state(my_id
, right_name
, right_sym
, &assigned
);
208 if (is_zero(expr
->right
)) {
209 slist
= get_possible_states(my_id
, left_name
, left_sym
);
211 FOR_EACH_PTR(slist
, tmp
) {
212 check_sm_is_leaked(tmp
);
213 } END_FOR_EACH_PTR(tmp
);
216 if (is_freed(left_name
, left_sym
)) {
217 set_state(my_id
, left_name
, left_sym
, &unfree
);
219 if (left_name
&& is_parent(left
))
220 assign_parent(left_sym
);
221 if (right_name
&& is_parent(right
))
222 assign_parent(right_sym
);
224 free_string(left_name
);
225 free_string(right_name
);
228 static int is_null(const char *name
, struct symbol
*sym
)
230 struct smatch_state
*state
;
232 state
= get_state(my_id
, name
, sym
);
233 if (state
&& !strcmp(state
->name
, "isnull"))
238 static void set_unfree(struct sm_state
*sm
, struct expression
*mod_expr
)
240 if (slist_has_state(sm
->possible
, &isfree
))
241 set_state(my_id
, sm
->name
, sm
->sym
, &unfree
);
244 static void match_free_func(const char *fn
, struct expression
*expr
, void *data
)
246 struct expression
*ptr_expr
;
248 struct symbol
*ptr_sym
;
249 int arg_num
= PTR_INT(data
);
251 ptr_expr
= get_argument_from_call_expr(expr
->args
, arg_num
);
252 ptr_name
= expr_to_var_sym(ptr_expr
, &ptr_sym
);
255 set_state(my_id
, ptr_name
, ptr_sym
, &isfree
);
256 free_string(ptr_name
);
259 static int possibly_allocated(struct state_list
*slist
)
261 struct sm_state
*tmp
;
263 FOR_EACH_PTR(slist
, tmp
) {
264 if (tmp
->state
== &allocated
)
266 if (tmp
->state
== &malloced
)
268 } END_FOR_EACH_PTR(tmp
);
272 static void check_sm_is_leaked(struct sm_state
*sm
)
274 if (possibly_allocated(sm
->possible
) &&
275 !is_null(sm
->name
, sm
->sym
) &&
276 !is_argument(sm
->sym
) &&
277 !parent_is_assigned(sm
->sym
))
278 sm_msg("error: memory leak of '%s'", sm
->name
);
281 static void check_tracker_is_leaked(struct tracker
*t
)
285 sm
= get_sm_state(t
->owner
, t
->name
, t
->sym
);
287 check_sm_is_leaked(sm
);
291 static void match_declarations(struct symbol
*sym
)
295 if ((get_base_type(sym
))->type
== SYM_ARRAY
) {
299 name
= sym
->ident
->name
;
301 if (sym
->initializer
) {
302 if (is_allocation(sym
->initializer
)) {
303 set_state(my_id
, name
, sym
, &malloced
);
304 add_scope_hook((scope_hook
*)&check_tracker_is_leaked
,
305 alloc_tracker(my_id
, name
, sym
));
306 scoped_state(my_id
, name
, sym
);
313 static void check_for_allocated(void)
316 struct sm_state
*tmp
;
318 stree
= __get_cur_stree();
319 FOR_EACH_MY_SM(my_id
, stree
, tmp
) {
320 check_sm_is_leaked(tmp
);
321 } END_FOR_EACH_SM(tmp
);
324 static void match_return(struct expression
*ret_value
)
331 name
= expr_to_str_sym(ret_value
, &sym
);
335 check_for_allocated();
338 static void set_new_true_false_paths(const char *name
, struct symbol
*sym
)
340 struct smatch_state
*tmp
;
342 tmp
= get_state(my_id
, name
, sym
);
348 if (tmp
== &isfree
) {
349 sm_msg("warn: why do you care about freed memory? '%s'", name
);
352 if (tmp
== &assigned
) {
353 /* we don't care about assigned pointers any more */
356 set_true_false_states(my_id
, name
, sym
, &allocated
, &isnull
);
359 static void match_condition(struct expression
*expr
)
364 expr
= strip_expr(expr
);
365 switch (expr
->type
) {
369 name
= expr_to_var_sym(expr
, &sym
);
372 set_new_true_false_paths(name
, sym
);
375 case EXPR_ASSIGNMENT
:
376 /* You have to deal with stuff like if (a = b = c) */
377 match_condition(expr
->right
);
378 match_condition(expr
->left
);
385 static void match_function_call(struct expression
*expr
)
387 struct expression
*tmp
;
390 struct sm_state
*state
;
392 FOR_EACH_PTR(expr
->args
, tmp
) {
393 tmp
= strip_expr(tmp
);
394 name
= expr_to_str_sym(tmp
, &sym
);
397 if ((state
= get_sm_state(my_id
, name
, sym
))) {
398 if (possibly_allocated(state
->possible
)) {
399 set_state(my_id
, name
, sym
, &assigned
);
404 } END_FOR_EACH_PTR(tmp
);
407 static void match_end_func(struct symbol
*sym
)
411 check_for_allocated();
412 free_trackers_and_list(&arguments
);
415 static void register_funcs_from_file(void)
421 token
= get_tokens_file("kernel.frees_argument");
424 if (token_type(token
) != TOKEN_STREAMBEGIN
)
427 while (token_type(token
) != TOKEN_STREAMEND
) {
428 if (token_type(token
) != TOKEN_IDENT
)
430 func
= show_ident(token
->ident
);
432 if (token_type(token
) != TOKEN_NUMBER
)
434 arg
= atoi(token
->number
);
435 add_function_hook(func
, &match_free_func
, INT_PTR(arg
));
441 void check_memory(int id
)
444 add_unmatched_state_hook(my_id
, &unmatched_state
);
445 add_hook(&match_function_def
, FUNC_DEF_HOOK
);
446 add_hook(&match_declarations
, DECLARATION_HOOK
);
447 add_hook(&match_function_call
, FUNCTION_CALL_HOOK
);
448 add_hook(&match_condition
, CONDITION_HOOK
);
449 add_hook(&match_assign
, ASSIGNMENT_HOOK
);
450 add_hook(&match_return
, RETURN_HOOK
);
451 add_hook(&match_end_func
, END_FUNC_HOOK
);
452 add_modification_hook(my_id
, &set_unfree
);
453 if (option_project
== PROJ_KERNEL
) {
454 add_function_hook("kfree", &match_free_func
, (void *)0);
455 register_funcs_from_file();
457 add_function_hook("free", &match_free_func
, (void *)0);