implied: handle &undefined better
[smatch.git] / check_memory.c
blob1cf9c77353c37ada87d970164f7917b2221aa0fc
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 "parse.h"
11 #include "smatch.h"
12 #include "smatch_slist.h"
14 static int my_id;
16 STATE(allocated);
17 STATE(assigned);
18 STATE(isfree);
19 STATE(malloced);
20 STATE(isnull);
21 STATE(unfree);
24 malloced --> allocated --> assigned --> isfree +
25 \-> isnull. \-> isfree +
27 isfree --> unfree.
28 \-> isnull.
31 struct tracker_list *arguments;
33 static const char *allocation_funcs[] = {
34 "malloc",
35 "kmalloc",
36 NULL,
39 static struct smatch_state *merge_func(const char *name, struct symbol *sym,
40 struct smatch_state *s1,
41 struct smatch_state *s2)
43 if (!strcmp(name, "-"))
44 return &assigned;
45 /* this is normal merge */
46 if (!s1 || !s2)
47 return &undefined;
48 return &merged;
51 static void assign_parent(struct symbol *sym)
53 set_state("-", my_id, sym, &assigned);
56 static int parent_is_assigned(struct symbol *sym)
58 struct smatch_state *state;
60 state = get_state("-", my_id, sym);
61 if (state == &assigned)
62 return 1;
63 return 0;
66 static int is_allocation(struct expression *expr)
68 char *fn_name;
69 int i;
71 if (expr->type != EXPR_CALL)
72 return 0;
74 if (!(fn_name = get_variable_from_expr(expr->fn, NULL)))
75 return 0;
77 for (i = 0; allocation_funcs[i]; i++) {
78 if (!strcmp(fn_name, allocation_funcs[i])) {
79 free_string(fn_name);
80 return 1;
83 free_string(fn_name);
84 return 0;
87 static int is_freed(const char *name, struct symbol *sym)
89 struct state_list *slist;
91 slist = get_possible_states(name, my_id, sym);
92 if (slist_has_state(slist, &isfree)) {
93 return 1;
95 return 0;
98 static int is_argument(struct symbol *sym)
100 struct tracker *arg;
102 FOR_EACH_PTR(arguments, arg) {
103 if (arg->sym == sym)
104 return 1;
105 } END_FOR_EACH_PTR(arg);
106 return 0;
109 static void match_function_def(struct symbol *sym)
111 struct symbol *arg;
113 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
114 add_tracker(&arguments, (arg->ident?arg->ident->name:"NULL"), my_id, arg);
115 } END_FOR_EACH_PTR(arg);
118 static void match_declarations(struct symbol *sym)
120 const char *name;
122 if ((get_base_type(sym))->type == SYM_ARRAY) {
123 return;
126 name = sym->ident->name;
128 if (sym->initializer) {
129 if (is_allocation(sym->initializer)) {
130 set_state(name, my_id, sym, &malloced);
131 } else {
132 assign_parent(sym);
137 static int is_parent(struct expression *expr)
139 if (expr->type == EXPR_DEREF)
140 return 0;
141 return 1;
144 static int assign_seen;
145 static int handle_double_assign(struct expression *expr)
147 struct symbol *sym;
148 char *name;
150 if (expr->right->type != EXPR_ASSIGNMENT)
151 return 0;
152 assign_seen++;
154 name = get_variable_from_expr(expr->left, &sym);
155 if (name && is_parent(expr->left))
156 assign_parent(sym);
158 name = get_variable_from_expr(expr->right->left, &sym);
159 if (name && is_parent(expr->right->left))
160 assign_parent(sym);
162 name = get_variable_from_expr(expr->right->right, &sym);
163 if (name && is_parent(expr->right->right))
164 assign_parent(sym);
166 return 1;
169 static void match_assign(struct expression *expr)
171 struct expression *left, *right;
172 char *left_name = NULL;
173 char *right_name = NULL;
174 struct symbol *left_sym, *right_sym;
175 struct smatch_state *state;
177 if (assign_seen) {
178 assign_seen--;
179 return;
182 if (handle_double_assign(expr)) {
183 return;
186 left = strip_expr(expr->left);
187 left_name = get_variable_from_expr_complex(left, &left_sym);
189 right = strip_expr(expr->right);
190 if (left_name && left_sym && is_allocation(right) &&
191 !(left_sym->ctype.modifiers &
192 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
193 !parent_is_assigned(left_sym)) {
194 set_state(left_name, my_id, left_sym, &malloced);
195 goto exit;
198 right_name = get_variable_from_expr_complex(right, &right_sym);
200 if (right_name && (state = get_state(right_name, my_id, right_sym))) {
201 if (state == &isfree)
202 smatch_msg("error: assigning freed pointer");
203 set_state(right_name, my_id, right_sym, &assigned);
206 if (is_freed(left_name, left_sym)) {
207 set_state(left_name, my_id, left_sym, &unfree);
209 if (left_name && is_parent(left))
210 assign_parent(left_sym);
211 if (right_name && is_parent(right))
212 assign_parent(right_sym);
213 exit:
214 free_string(left_name);
215 free_string(right_name);
218 static int is_null(char *name, struct symbol *sym)
220 struct smatch_state *state;
223 * FIXME. Ha ha ha... This is so wrong.
224 * I'm pulling in data from the check_null_deref script.
225 * I just happen to know that its ID is 3.
226 * The correct approved way to do this is to get the data from
227 * smatch_extra. But right now smatch_extra doesn't track it.
229 state = get_state(name, my_id, sym);
230 if (state && !strcmp(state->name, "isnull"))
231 return 1;
232 return 0;
235 static void match_kfree(struct expression *expr)
237 struct expression *ptr_expr;
238 char *ptr_name;
239 struct symbol *ptr_sym;
241 ptr_expr = get_argument_from_call_expr(expr->args, 0);
242 ptr_name = get_variable_from_expr(ptr_expr, &ptr_sym);
243 if (is_freed(ptr_name, ptr_sym) && !is_null(ptr_name, ptr_sym)) {
244 smatch_msg("error: double free of %s", ptr_name);
246 set_state(ptr_name, my_id, ptr_sym, &isfree);
247 free_string(ptr_name);
250 static int possibly_allocated(struct state_list *slist)
252 struct sm_state *tmp;
254 FOR_EACH_PTR(slist, tmp) {
255 if (tmp->state == &allocated)
256 return 1;
257 if (tmp->state == &malloced)
258 return 1;
259 } END_FOR_EACH_PTR(tmp);
260 return 0;
263 static void check_for_allocated()
265 struct state_list *slist;
266 struct sm_state *tmp;
268 slist = get_all_states(my_id);
269 FOR_EACH_PTR(slist, tmp) {
270 if (possibly_allocated(tmp->possible) &&
271 !is_null(tmp->name, tmp->sym) &&
272 !is_argument(tmp->sym) &&
273 !parent_is_assigned(tmp->sym))
274 smatch_msg("error: memery leak of %s", tmp->name);
275 } END_FOR_EACH_PTR(tmp);
276 free_slist(&slist);
279 static void match_return(struct statement *stmt)
281 char *name;
282 struct symbol *sym;
284 name = get_variable_from_expr(stmt->ret_value, &sym);
285 if (sym)
286 assign_parent(sym);
287 free_string(name);
288 check_for_allocated();
291 static void set_new_true_false_paths(const char *name, struct symbol *sym)
293 struct smatch_state *tmp;
295 tmp = get_state(name, my_id, sym);
297 if (!tmp) {
298 return;
301 if (tmp == &isfree) {
302 smatch_msg("warn: why do you care about freed memory?");
305 if (tmp == &assigned) {
306 /* we don't care about assigned pointers any more */
307 return;
309 set_true_false_states(name, my_id, sym, &allocated, &isnull);
312 static void match_condition(struct expression *expr)
314 struct symbol *sym;
315 char *name;
317 expr = strip_expr(expr);
318 switch(expr->type) {
319 case EXPR_PREOP:
320 case EXPR_SYMBOL:
321 case EXPR_DEREF:
322 name = get_variable_from_expr(expr, &sym);
323 if (!name)
324 return;
325 set_new_true_false_paths(name, sym);
326 free_string(name);
327 return;
328 case EXPR_ASSIGNMENT:
329 assign_seen++;
330 /* You have to deal with stuff like if (a = b = c) */
331 match_condition(expr->right);
332 match_condition(expr->left);
333 return;
334 default:
335 return;
339 static void match_function_call(struct expression *expr)
341 struct expression *tmp;
342 struct symbol *sym;
343 char *name;
344 char *fn_name;
345 struct sm_state *state;
347 fn_name = get_variable_from_expr(expr->fn, NULL);
349 if (fn_name && !strcmp(fn_name, "kfree")) {
350 match_kfree(expr);
353 FOR_EACH_PTR(expr->args, tmp) {
354 tmp = strip_expr(tmp);
355 name = get_variable_from_expr(tmp, &sym);
356 if (!name)
357 continue;
358 if ((state = get_sm_state(name, my_id, sym))) {
359 if (possibly_allocated(state->possible)) {
360 set_state(name, my_id, sym, &assigned);
363 assign_parent(sym);
364 } END_FOR_EACH_PTR(tmp);
367 static void match_end_func(struct symbol *sym)
369 check_for_allocated();
370 free_trackers_and_list(&arguments);
373 void register_memory(int id)
375 my_id = id;
376 add_merge_hook(my_id, &merge_func);
377 add_hook(&match_function_def, FUNC_DEF_HOOK);
378 add_hook(&match_declarations, DECLARATION_HOOK);
379 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
380 add_hook(&match_condition, CONDITION_HOOK);
381 add_hook(&match_assign, ASSIGNMENT_HOOK);
382 add_hook(&match_return, RETURN_HOOK);
383 add_hook(&match_end_func, END_FUNC_HOOK);