handle known conditions better
[smatch.git] / check_memory.c
blob864230beb836a7c7ef3222c8e8469f94f9f13b08
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 static struct tracker_list *arguments;
33 static const char *allocation_funcs[] = {
34 "malloc",
35 "kmalloc",
36 "kzalloc",
37 NULL,
40 static struct smatch_state *merge_func(const char *name, struct symbol *sym,
41 struct smatch_state *s1,
42 struct smatch_state *s2)
44 if (!strcmp(name, "-"))
45 return &assigned;
46 /* this is normal merge */
47 if (!s1 || !s2)
48 return &undefined;
49 return &merged;
52 static void assign_parent(struct symbol *sym)
54 set_state("-", my_id, sym, &assigned);
57 static int parent_is_assigned(struct symbol *sym)
59 struct smatch_state *state;
61 state = get_state("-", my_id, sym);
62 if (state == &assigned)
63 return 1;
64 return 0;
67 static int is_allocation(struct expression *expr)
69 char *fn_name;
70 int i;
72 if (expr->type != EXPR_CALL)
73 return 0;
75 if (!(fn_name = get_variable_from_expr(expr->fn, NULL)))
76 return 0;
78 for (i = 0; allocation_funcs[i]; i++) {
79 if (!strcmp(fn_name, allocation_funcs[i])) {
80 free_string(fn_name);
81 return 1;
84 free_string(fn_name);
85 return 0;
88 static int is_freed(const char *name, struct symbol *sym)
90 struct state_list *slist;
92 slist = get_possible_states(name, my_id, sym);
93 if (slist_has_state(slist, &isfree)) {
94 return 1;
96 return 0;
99 static int is_argument(struct symbol *sym)
101 struct tracker *arg;
103 FOR_EACH_PTR(arguments, arg) {
104 if (arg->sym == sym)
105 return 1;
106 } END_FOR_EACH_PTR(arg);
107 return 0;
110 static void match_function_def(struct symbol *sym)
112 struct symbol *arg;
114 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
115 add_tracker(&arguments, (arg->ident?arg->ident->name:"NULL"), my_id, arg);
116 } END_FOR_EACH_PTR(arg);
119 static void match_declarations(struct symbol *sym)
121 const char *name;
123 if ((get_base_type(sym))->type == SYM_ARRAY) {
124 return;
127 name = sym->ident->name;
129 if (sym->initializer) {
130 if (is_allocation(sym->initializer)) {
131 set_state(name, my_id, sym, &malloced);
132 } else {
133 assign_parent(sym);
138 static int is_parent(struct expression *expr)
140 if (expr->type == EXPR_DEREF)
141 return 0;
142 return 1;
145 static int assign_seen;
146 static int handle_double_assign(struct expression *expr)
148 struct symbol *sym;
149 char *name;
151 if (expr->right->type != EXPR_ASSIGNMENT)
152 return 0;
153 assign_seen++;
155 name = get_variable_from_expr_complex(expr->left, &sym);
156 if (name && is_parent(expr->left))
157 assign_parent(sym);
159 name = get_variable_from_expr_complex(expr->right->left, &sym);
160 if (name && is_parent(expr->right->left))
161 assign_parent(sym);
163 name = get_variable_from_expr_complex(expr->right->right, &sym);
164 if (name && is_parent(expr->right->right))
165 assign_parent(sym);
167 return 1;
170 static void match_assign(struct expression *expr)
172 struct expression *left, *right;
173 char *left_name = NULL;
174 char *right_name = NULL;
175 struct symbol *left_sym, *right_sym;
176 struct smatch_state *state;
178 if (assign_seen) {
179 assign_seen--;
180 return;
183 if (handle_double_assign(expr)) {
184 return;
187 left = strip_expr(expr->left);
188 left_name = get_variable_from_expr_complex(left, &left_sym);
190 right = strip_expr(expr->right);
191 if (left_name && left_sym && is_allocation(right) &&
192 !(left_sym->ctype.modifiers &
193 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
194 !parent_is_assigned(left_sym)) {
195 set_state(left_name, my_id, left_sym, &malloced);
196 goto exit;
199 right_name = get_variable_from_expr_complex(right, &right_sym);
201 if (right_name && (state = get_state(right_name, my_id, right_sym))) {
202 if (state == &isfree)
203 smatch_msg("error: assigning freed pointer");
204 set_state(right_name, my_id, right_sym, &assigned);
207 if (is_freed(left_name, left_sym)) {
208 set_state(left_name, my_id, left_sym, &unfree);
210 if (left_name && is_parent(left))
211 assign_parent(left_sym);
212 if (right_name && is_parent(right))
213 assign_parent(right_sym);
214 exit:
215 free_string(left_name);
216 free_string(right_name);
219 static int is_null(char *name, struct symbol *sym)
221 struct smatch_state *state;
224 * FIXME. Ha ha ha... This is so wrong.
225 * I'm pulling in data from the check_null_deref script.
226 * I just happen to know that its ID is 3.
227 * The correct approved way to do this is to get the data from
228 * smatch_extra. But right now smatch_extra doesn't track it.
230 state = get_state(name, my_id, sym);
231 if (state && !strcmp(state->name, "isnull"))
232 return 1;
233 return 0;
236 static void match_kfree(struct expression *expr)
238 struct expression *ptr_expr;
239 char *ptr_name;
240 struct symbol *ptr_sym;
242 ptr_expr = get_argument_from_call_expr(expr->args, 0);
243 ptr_name = get_variable_from_expr_complex(ptr_expr, &ptr_sym);
244 if (is_freed(ptr_name, ptr_sym) && !is_null(ptr_name, ptr_sym)) {
245 smatch_msg("error: double free of %s", ptr_name);
247 set_state(ptr_name, my_id, ptr_sym, &isfree);
248 free_string(ptr_name);
251 static int possibly_allocated(struct state_list *slist)
253 struct sm_state *tmp;
255 FOR_EACH_PTR(slist, tmp) {
256 if (tmp->state == &allocated)
257 return 1;
258 if (tmp->state == &malloced)
259 return 1;
260 } END_FOR_EACH_PTR(tmp);
261 return 0;
264 static void check_for_allocated()
266 struct state_list *slist;
267 struct sm_state *tmp;
269 slist = get_all_states(my_id);
270 FOR_EACH_PTR(slist, tmp) {
271 if (possibly_allocated(tmp->possible) &&
272 !is_null(tmp->name, tmp->sym) &&
273 !is_argument(tmp->sym) &&
274 !parent_is_assigned(tmp->sym))
275 smatch_msg("error: memery leak of %s", tmp->name);
276 } END_FOR_EACH_PTR(tmp);
277 free_slist(&slist);
280 static void match_return(struct statement *stmt)
282 char *name;
283 struct symbol *sym;
285 name = get_variable_from_expr_complex(stmt->ret_value, &sym);
286 if (sym)
287 assign_parent(sym);
288 free_string(name);
289 check_for_allocated();
292 static void set_new_true_false_paths(const char *name, struct symbol *sym)
294 struct smatch_state *tmp;
296 tmp = get_state(name, my_id, sym);
298 if (!tmp) {
299 return;
302 if (tmp == &isfree) {
303 smatch_msg("warn: why do you care about freed memory?");
306 if (tmp == &assigned) {
307 /* we don't care about assigned pointers any more */
308 return;
310 set_true_false_states(name, my_id, sym, &allocated, &isnull);
313 static void match_condition(struct expression *expr)
315 struct symbol *sym;
316 char *name;
318 expr = strip_expr(expr);
319 switch(expr->type) {
320 case EXPR_PREOP:
321 case EXPR_SYMBOL:
322 case EXPR_DEREF:
323 name = get_variable_from_expr_complex(expr, &sym);
324 if (!name)
325 return;
326 set_new_true_false_paths(name, sym);
327 free_string(name);
328 return;
329 case EXPR_ASSIGNMENT:
330 assign_seen++;
331 /* You have to deal with stuff like if (a = b = c) */
332 match_condition(expr->right);
333 match_condition(expr->left);
334 return;
335 default:
336 return;
340 static void match_function_call(struct expression *expr)
342 struct expression *tmp;
343 struct symbol *sym;
344 char *name;
345 char *fn_name;
346 struct sm_state *state;
348 fn_name = get_variable_from_expr(expr->fn, NULL);
350 if (fn_name && !strcmp(fn_name, "kfree")) {
351 match_kfree(expr);
354 FOR_EACH_PTR(expr->args, tmp) {
355 tmp = strip_expr(tmp);
356 name = get_variable_from_expr_complex(tmp, &sym);
357 if (!name)
358 continue;
359 if ((state = get_sm_state(name, my_id, sym))) {
360 if (possibly_allocated(state->possible)) {
361 set_state(name, my_id, sym, &assigned);
364 assign_parent(sym);
365 } END_FOR_EACH_PTR(tmp);
368 static void match_end_func(struct symbol *sym)
370 check_for_allocated();
371 free_trackers_and_list(&arguments);
374 void register_memory(int id)
376 my_id = id;
377 add_merge_hook(my_id, &merge_func);
378 add_hook(&match_function_def, FUNC_DEF_HOOK);
379 add_hook(&match_declarations, DECLARATION_HOOK);
380 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
381 add_hook(&match_condition, CONDITION_HOOK);
382 add_hook(&match_assign, ASSIGNMENT_HOOK);
383 add_hook(&match_return, RETURN_HOOK);
384 add_hook(&match_end_func, END_FUNC_HOOK);