flow: introduce definite_inside_loop()
[smatch.git] / check_leaks.c
blob95e5c92e3b074f6c3965e2065102d7ffb4f48250
1 /*
2 * sparse/check_leaks.c
4 * Copyright (C) 2010 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
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();
20 * foo = something();
22 * The work around is that for now what this check only
23 * checks simple expressions and doesn't check whether
24 * foo->bar is leaked.
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include "parse.h"
31 #include "smatch.h"
32 #include "smatch_slist.h"
34 static int my_id;
36 STATE(allocated);
37 STATE(ok);
39 static void set_parent(struct expression *expr, struct smatch_state *state);
41 static const char *allocation_funcs[] = {
42 "malloc",
43 "kmalloc",
44 "kzalloc",
45 "kmemdup",
48 static char *alloc_parent_str(struct symbol *sym)
50 static char buf[256];
52 if (!sym || !sym->ident)
53 return NULL;
55 snprintf(buf, 255, "%s", sym->ident->name);
56 buf[255] = '\0';
57 return alloc_string(buf);
60 static char *get_parent_from_expr(struct expression *expr, struct symbol **sym)
62 char *name;
64 expr = strip_expr(expr);
66 name = expr_to_str_sym(expr, sym);
67 free_string(name);
68 if (!name || !*sym || !(*sym)->ident) {
69 *sym = NULL;
70 return NULL;
72 return alloc_parent_str(*sym);
75 static int is_local(struct expression *expr)
77 char *name;
78 struct symbol *sym;
79 int ret = 0;
81 name = expr_to_str_sym(expr, &sym);
82 if (!name || !sym)
83 goto out;
84 if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
85 goto out;
86 ret = 1;
87 out:
88 free_string(name);
89 return ret;
92 static int is_param(struct expression *expr)
94 char *name;
95 struct symbol *sym;
96 struct symbol *tmp;
97 int ret = 0;
99 name = expr_to_str_sym(expr, &sym);
100 if (!name || !sym)
101 goto out;
102 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
103 if (tmp == sym) {
104 ret = 1;
105 goto out;
107 } END_FOR_EACH_PTR(tmp);
108 out:
109 free_string(name);
110 return ret;
114 static void match_alloc(const char *fn, struct expression *expr, void *unused)
116 if (!is_local(expr->left))
117 return;
118 if (is_param(expr->left))
119 return;
120 if (expr->left->type != EXPR_SYMBOL)
121 return;
122 set_state_expr(my_id, expr->left, &allocated);
125 static void match_condition(struct expression *expr)
127 struct sm_state *sm;
129 expr = strip_expr(expr);
131 switch (expr->type) {
132 case EXPR_PREOP:
133 case EXPR_SYMBOL:
134 case EXPR_DEREF:
135 sm = get_sm_state_expr(my_id, expr);
136 if (sm && slist_has_state(sm->possible, &allocated))
137 set_true_false_states_expr(my_id, expr, &allocated, &ok);
138 return;
139 case EXPR_ASSIGNMENT:
140 /* You have to deal with stuff like if (a = b = c) */
141 match_condition(expr->left);
142 return;
143 default:
144 return;
148 static void set_parent(struct expression *expr, struct smatch_state *state)
150 char *name;
151 struct symbol *sym;
153 name = get_parent_from_expr(expr, &sym);
154 if (!name || !sym)
155 goto free;
156 if (state == &ok && !get_state(my_id, name, sym))
157 goto free;
158 set_state(my_id, name, sym, state);
159 free:
160 free_string(name);
163 static void match_function_call(struct expression *expr)
165 struct expression *tmp;
167 FOR_EACH_PTR(expr->args, tmp) {
168 set_parent(tmp, &ok);
169 } END_FOR_EACH_PTR(tmp);
172 static void warn_if_allocated(struct expression *expr)
174 struct sm_state *sm;
175 char *name;
177 sm = get_sm_state_expr(my_id, expr);
178 if (!sm)
179 return;
180 if (!slist_has_state(sm->possible, &allocated))
181 return;
183 name = expr_to_var(expr);
184 sm_msg("warn: overwrite may leak '%s'", name);
185 free_string(name);
187 /* silence further warnings */
188 set_state_expr(my_id, expr, &ok);
191 static void match_assign(struct expression *expr)
193 struct expression *right;
195 right = expr->right;
197 while (right->type == EXPR_ASSIGNMENT)
198 right = right->left;
200 warn_if_allocated(expr->left);
201 set_parent(right, &ok);
204 static void check_for_allocated(void)
206 struct state_list *slist;
207 struct sm_state *tmp;
209 slist = get_all_states(my_id);
210 FOR_EACH_PTR(slist, tmp) {
211 if (!slist_has_state(tmp->possible, &allocated))
212 continue;
213 sm_msg("warn: possible memory leak of '%s'", tmp->name);
214 } END_FOR_EACH_PTR(tmp);
215 free_slist(&slist);
218 static void match_return(struct expression *ret_value)
220 if (__inline_fn)
221 return;
222 set_parent(ret_value, &ok);
223 check_for_allocated();
226 static void match_end_func(struct symbol *sym)
228 if (__inline_fn)
229 return;
230 check_for_allocated();
233 void check_leaks(int id)
235 int i;
237 my_id = id;
239 for (i = 0; i < ARRAY_SIZE(allocation_funcs); i++)
240 add_function_assign_hook(allocation_funcs[i], &match_alloc, NULL);
242 add_hook(&match_condition, CONDITION_HOOK);
244 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
245 add_hook(&match_assign, ASSIGNMENT_HOOK);
247 add_hook(&match_return, RETURN_HOOK);
248 add_hook(&match_end_func, END_FUNC_HOOK);