implied cleanup: get rid of some curly braces
[smatch.git] / check_memory.c
blob8a7729fbdaecbc6844421a008eecd1dfd47a134b
1 /*
2 * sparse/check_memory.c
4 * Copyright (C) 2008 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
10 #include <fcntl.h>
11 #include <unistd.h>
12 #include "parse.h"
13 #include "smatch.h"
14 #include "smatch_slist.h"
16 static int my_id;
18 STATE(allocated);
19 STATE(assigned);
20 STATE(isfree);
21 STATE(malloced);
22 STATE(isnull);
23 STATE(unfree);
26 malloced --> allocated --> assigned --> isfree +
27 \-> isnull. \-> isfree +
29 isfree --> unfree.
30 \-> isnull.
33 static struct tracker_list *arguments;
35 static const char *allocation_funcs[] = {
36 "malloc",
37 "kmalloc",
38 "kzalloc",
39 NULL,
42 static char *get_parent_name(struct symbol *sym)
44 static char buf[256];
46 if (!sym || !sym->ident)
47 return NULL;
49 snprintf(buf, 255, "-%s", sym->ident->name);
50 buf[255] = '\0';
51 return alloc_string(buf);
54 static int is_parent_sym(const char *name)
56 if (!strncmp(name, "-", 1))
57 return 1;
58 return 0;
61 static struct smatch_state *unmatched_state(struct sm_state *sm)
63 if (is_parent_sym(sm->name))
64 return &assigned;
65 return &undefined;
68 static void assign_parent(struct symbol *sym)
70 char *name;
72 name = get_parent_name(sym);
73 if (!name)
74 return;
75 set_state(my_id, name, sym, &assigned);
76 free_string(name);
79 static int parent_is_assigned(struct symbol *sym)
81 struct smatch_state *state;
82 char *name;
84 name = get_parent_name(sym);
85 if (!name)
86 return 0;
87 state = get_state(my_id, name, sym);
88 free_string(name);
89 if (state == &assigned)
90 return 1;
91 return 0;
94 static int is_allocation(struct expression *expr)
96 char *fn_name;
97 int i;
99 if (expr->type != EXPR_CALL)
100 return 0;
102 if (!(fn_name = get_variable_from_expr(expr->fn, NULL)))
103 return 0;
105 for (i = 0; allocation_funcs[i]; i++) {
106 if (!strcmp(fn_name, allocation_funcs[i])) {
107 free_string(fn_name);
108 return 1;
111 free_string(fn_name);
112 return 0;
115 static int is_freed(const char *name, struct symbol *sym)
117 struct state_list *slist;
119 slist = get_possible_states(my_id, name, sym);
120 if (slist_has_state(slist, &isfree)) {
121 return 1;
123 return 0;
126 static int is_argument(struct symbol *sym)
128 struct tracker *arg;
130 FOR_EACH_PTR(arguments, arg) {
131 if (arg->sym == sym)
132 return 1;
133 } END_FOR_EACH_PTR(arg);
134 return 0;
137 static void match_function_def(struct symbol *sym)
139 struct symbol *arg;
141 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
142 add_tracker(&arguments, my_id, (arg->ident?arg->ident->name:"NULL"), arg);
143 } END_FOR_EACH_PTR(arg);
146 static int is_parent(struct expression *expr)
148 if (expr->type == EXPR_DEREF)
149 return 0;
150 return 1;
153 static void match_assign(struct expression *expr)
155 struct expression *left, *right;
156 char *left_name = NULL;
157 char *right_name = NULL;
158 struct symbol *left_sym, *right_sym;
159 struct smatch_state *state;
161 left = strip_expr(expr->left);
162 left_name = get_variable_from_expr_complex(left, &left_sym);
164 right = strip_expr(expr->right);
165 if (left_name && left_sym && is_allocation(right) &&
166 !(left_sym->ctype.modifiers &
167 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
168 !parent_is_assigned(left_sym)) {
169 set_state(my_id, left_name, left_sym, &malloced);
170 goto exit;
173 right_name = get_variable_from_expr_complex(right, &right_sym);
175 if (right_name && (state = get_state(my_id, right_name, right_sym))) {
176 if (state == &isfree)
177 sm_msg("error: assigning freed pointer");
178 set_state(my_id, right_name, right_sym, &assigned);
181 if (is_freed(left_name, left_sym)) {
182 set_state(my_id, left_name, left_sym, &unfree);
184 if (left_name && is_parent(left))
185 assign_parent(left_sym);
186 if (right_name && is_parent(right))
187 assign_parent(right_sym);
188 exit:
189 free_string(left_name);
190 free_string(right_name);
193 static int is_null(const char *name, struct symbol *sym)
195 struct smatch_state *state;
197 state = get_state(my_id, name, sym);
198 if (state && !strcmp(state->name, "isnull"))
199 return 1;
200 return 0;
203 static void set_unfree(const char *name, struct symbol *sym, struct expression *expr, void *unused)
205 set_state(my_id, name, sym, &unfree);
208 static void match_free_func(const char *fn, struct expression *expr, void *data)
210 struct expression *ptr_expr;
211 char *ptr_name;
212 struct symbol *ptr_sym;
213 int arg_num = PTR_INT(data);
215 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
216 ptr_name = get_variable_from_expr_complex(ptr_expr, &ptr_sym);
217 if (!ptr_name)
218 return;
219 if (is_freed(ptr_name, ptr_sym) && !is_null(ptr_name, ptr_sym)) {
220 sm_msg("error: double free of %s", ptr_name);
222 set_state(my_id, ptr_name, ptr_sym, &isfree);
223 add_modification_hook(ptr_name, &set_unfree, NULL);
224 free_string(ptr_name);
227 static int possibly_allocated(struct state_list *slist)
229 struct sm_state *tmp;
231 FOR_EACH_PTR(slist, tmp) {
232 if (tmp->state == &allocated)
233 return 1;
234 if (tmp->state == &malloced)
235 return 1;
236 } END_FOR_EACH_PTR(tmp);
237 return 0;
240 static void check_sm_is_leaked(struct sm_state *sm)
242 if (possibly_allocated(sm->possible) &&
243 !is_null(sm->name, sm->sym) &&
244 !is_argument(sm->sym) &&
245 !parent_is_assigned(sm->sym))
246 sm_msg("error: memory leak of %s", sm->name);
249 static void check_tracker_is_leaked(struct tracker *t)
251 struct sm_state *sm;
253 sm = get_sm_state(t->owner, t->name, t->sym);
254 if (sm)
255 check_sm_is_leaked(sm);
256 __free_tracker(t);
259 static void match_declarations(struct symbol *sym)
261 const char *name;
263 if ((get_base_type(sym))->type == SYM_ARRAY) {
264 return;
267 name = sym->ident->name;
269 if (sym->initializer) {
270 if (is_allocation(sym->initializer)) {
271 set_state(my_id, name, sym, &malloced);
272 add_scope_hook((scope_hook *)&check_tracker_is_leaked,
273 alloc_tracker(my_id, name, sym));
274 scoped_state(my_id, name, sym);
275 } else {
276 assign_parent(sym);
281 static void check_for_allocated(void)
283 struct state_list *slist;
284 struct sm_state *tmp;
286 slist = get_all_states(my_id);
287 FOR_EACH_PTR(slist, tmp) {
288 check_sm_is_leaked(tmp);
289 } END_FOR_EACH_PTR(tmp);
290 free_slist(&slist);
293 static void match_return(struct expression *ret_value)
295 char *name;
296 struct symbol *sym;
298 name = get_variable_from_expr_complex(ret_value, &sym);
299 if (sym)
300 assign_parent(sym);
301 free_string(name);
302 check_for_allocated();
305 static void set_new_true_false_paths(const char *name, struct symbol *sym)
307 struct smatch_state *tmp;
309 tmp = get_state(my_id, name, sym);
311 if (!tmp) {
312 return;
315 if (tmp == &isfree) {
316 sm_msg("warn: why do you care about freed memory?");
319 if (tmp == &assigned) {
320 /* we don't care about assigned pointers any more */
321 return;
323 set_true_false_states(my_id, name, sym, &allocated, &isnull);
326 static void match_condition(struct expression *expr)
328 struct symbol *sym;
329 char *name;
331 expr = strip_expr(expr);
332 switch(expr->type) {
333 case EXPR_PREOP:
334 case EXPR_SYMBOL:
335 case EXPR_DEREF:
336 name = get_variable_from_expr_complex(expr, &sym);
337 if (!name)
338 return;
339 set_new_true_false_paths(name, sym);
340 free_string(name);
341 return;
342 case EXPR_ASSIGNMENT:
343 /* You have to deal with stuff like if (a = b = c) */
344 match_condition(expr->right);
345 match_condition(expr->left);
346 return;
347 default:
348 return;
352 static void match_function_call(struct expression *expr)
354 struct expression *tmp;
355 struct symbol *sym;
356 char *name;
357 struct sm_state *state;
359 FOR_EACH_PTR(expr->args, tmp) {
360 tmp = strip_expr(tmp);
361 name = get_variable_from_expr_complex(tmp, &sym);
362 if (!name)
363 continue;
364 if ((state = get_sm_state(my_id, name, sym))) {
365 if (possibly_allocated(state->possible)) {
366 set_state(my_id, name, sym, &assigned);
369 assign_parent(sym);
370 free_string(name);
371 } END_FOR_EACH_PTR(tmp);
374 static void match_dereferences(struct expression *expr)
376 char *deref = NULL;
377 struct symbol *sym = NULL;
379 if (expr->type != EXPR_PREOP)
380 return;
381 expr = strip_expr(expr->unop);
383 deref = get_variable_from_expr(expr, &sym);
384 if (!deref)
385 return;
386 if (is_freed(deref, sym)) {
387 sm_msg("error: dereferencing freed memory '%s'", deref);
388 set_state(my_id, deref, sym, &unfree);
390 free_string(deref);
393 static void match_end_func(struct symbol *sym)
395 check_for_allocated();
396 free_trackers_and_list(&arguments);
399 static void register_funcs_from_file(void)
401 struct token *token;
402 const char *func;
403 int arg;
405 token = get_tokens_file("kernel.frees_argument");
406 if (!token)
407 return;
408 if (token_type(token) != TOKEN_STREAMBEGIN)
409 return;
410 token = token->next;
411 while (token_type(token) != TOKEN_STREAMEND) {
412 if (token_type(token) != TOKEN_IDENT)
413 return;
414 func = show_ident(token->ident);
415 token = token->next;
416 if (token_type(token) != TOKEN_NUMBER)
417 return;
418 arg = atoi(token->number);
419 add_function_hook(func, &match_free_func, INT_PTR(arg));
420 token = token->next;
422 clear_token_alloc();
425 void check_memory(int id)
427 my_id = id;
428 add_unmatched_state_hook(my_id, &unmatched_state);
429 add_hook(&match_function_def, FUNC_DEF_HOOK);
430 add_hook(&match_declarations, DECLARATION_HOOK);
431 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
432 add_hook(&match_condition, CONDITION_HOOK);
433 add_hook(&match_dereferences, DEREF_HOOK);
434 add_hook(&match_assign, ASSIGNMENT_HOOK);
435 add_hook(&match_return, RETURN_HOOK);
436 add_hook(&match_end_func, END_FUNC_HOOK);
437 if (option_project == PROJ_KERNEL)
438 add_function_hook("kfree", &match_free_func, (void *)0);
439 else
440 add_function_hook("free", &match_free_func, (void *)0);
441 register_funcs_from_file();