core: improve anding condition handling.
[smatch.git] / check_memory.c
blobadd230c066eee3259db0b43c33779f6717845220
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 *unmatched_state(struct sm_state *sm)
42 if (!strcmp(sm->name, "-"))
43 return &assigned;
44 return &undefined;
47 static void assign_parent(struct symbol *sym)
49 set_state("-", my_id, sym, &assigned);
52 static int parent_is_assigned(struct symbol *sym)
54 struct smatch_state *state;
56 state = get_state("-", my_id, sym);
57 if (state == &assigned)
58 return 1;
59 return 0;
62 static int is_allocation(struct expression *expr)
64 char *fn_name;
65 int i;
67 if (expr->type != EXPR_CALL)
68 return 0;
70 if (!(fn_name = get_variable_from_expr(expr->fn, NULL)))
71 return 0;
73 for (i = 0; allocation_funcs[i]; i++) {
74 if (!strcmp(fn_name, allocation_funcs[i])) {
75 free_string(fn_name);
76 return 1;
79 free_string(fn_name);
80 return 0;
83 static int is_freed(const char *name, struct symbol *sym)
85 struct state_list *slist;
87 slist = get_possible_states(name, my_id, sym);
88 if (slist_has_state(slist, &isfree)) {
89 return 1;
91 return 0;
94 static int is_argument(struct symbol *sym)
96 struct tracker *arg;
98 FOR_EACH_PTR(arguments, arg) {
99 if (arg->sym == sym)
100 return 1;
101 } END_FOR_EACH_PTR(arg);
102 return 0;
105 static void match_function_def(struct symbol *sym)
107 struct symbol *arg;
109 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
110 add_tracker(&arguments, (arg->ident?arg->ident->name:"NULL"), my_id, arg);
111 } END_FOR_EACH_PTR(arg);
114 static void match_declarations(struct symbol *sym)
116 const char *name;
118 if ((get_base_type(sym))->type == SYM_ARRAY) {
119 return;
122 name = sym->ident->name;
124 if (sym->initializer) {
125 if (is_allocation(sym->initializer)) {
126 set_state(name, my_id, sym, &malloced);
127 } else {
128 assign_parent(sym);
133 static int is_parent(struct expression *expr)
135 if (expr->type == EXPR_DEREF)
136 return 0;
137 return 1;
140 static int assign_seen;
141 static int handle_double_assign(struct expression *expr)
143 struct symbol *sym;
144 char *name;
146 if (expr->right->type != EXPR_ASSIGNMENT)
147 return 0;
148 assign_seen++;
150 name = get_variable_from_expr_complex(expr->left, &sym);
151 if (name && is_parent(expr->left))
152 assign_parent(sym);
154 name = get_variable_from_expr_complex(expr->right->left, &sym);
155 if (name && is_parent(expr->right->left))
156 assign_parent(sym);
158 name = get_variable_from_expr_complex(expr->right->right, &sym);
159 if (name && is_parent(expr->right->right))
160 assign_parent(sym);
162 return 1;
165 static void match_assign(struct expression *expr)
167 struct expression *left, *right;
168 char *left_name = NULL;
169 char *right_name = NULL;
170 struct symbol *left_sym, *right_sym;
171 struct smatch_state *state;
173 if (assign_seen) {
174 assign_seen--;
175 return;
178 if (handle_double_assign(expr)) {
179 return;
182 left = strip_expr(expr->left);
183 left_name = get_variable_from_expr_complex(left, &left_sym);
185 right = strip_expr(expr->right);
186 if (left_name && left_sym && is_allocation(right) &&
187 !(left_sym->ctype.modifiers &
188 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
189 !parent_is_assigned(left_sym)) {
190 set_state(left_name, my_id, left_sym, &malloced);
191 goto exit;
194 right_name = get_variable_from_expr_complex(right, &right_sym);
196 if (right_name && (state = get_state(right_name, my_id, right_sym))) {
197 if (state == &isfree)
198 smatch_msg("error: assigning freed pointer");
199 set_state(right_name, my_id, right_sym, &assigned);
202 if (is_freed(left_name, left_sym)) {
203 set_state(left_name, my_id, left_sym, &unfree);
205 if (left_name && is_parent(left))
206 assign_parent(left_sym);
207 if (right_name && is_parent(right))
208 assign_parent(right_sym);
209 exit:
210 free_string(left_name);
211 free_string(right_name);
214 static int is_null(char *name, struct symbol *sym)
216 struct smatch_state *state;
219 * FIXME. Ha ha ha... This is so wrong.
220 * I'm pulling in data from the check_null_deref script.
221 * I just happen to know that its ID is 3.
222 * The correct approved way to do this is to get the data from
223 * smatch_extra. But right now smatch_extra doesn't track it.
225 state = get_state(name, my_id, sym);
226 if (state && !strcmp(state->name, "isnull"))
227 return 1;
228 return 0;
231 static void match_kfree(struct expression *expr)
233 struct expression *ptr_expr;
234 char *ptr_name;
235 struct symbol *ptr_sym;
237 ptr_expr = get_argument_from_call_expr(expr->args, 0);
238 ptr_name = get_variable_from_expr_complex(ptr_expr, &ptr_sym);
239 if (is_freed(ptr_name, ptr_sym) && !is_null(ptr_name, ptr_sym)) {
240 smatch_msg("error: double free of %s", ptr_name);
242 set_state(ptr_name, my_id, ptr_sym, &isfree);
243 free_string(ptr_name);
246 static int possibly_allocated(struct state_list *slist)
248 struct sm_state *tmp;
250 FOR_EACH_PTR(slist, tmp) {
251 if (tmp->state == &allocated)
252 return 1;
253 if (tmp->state == &malloced)
254 return 1;
255 } END_FOR_EACH_PTR(tmp);
256 return 0;
259 static void check_for_allocated(void)
261 struct state_list *slist;
262 struct sm_state *tmp;
264 slist = get_all_states(my_id);
265 FOR_EACH_PTR(slist, tmp) {
266 if (possibly_allocated(tmp->possible) &&
267 !is_null(tmp->name, tmp->sym) &&
268 !is_argument(tmp->sym) &&
269 !parent_is_assigned(tmp->sym))
270 smatch_msg("error: memery leak of %s", tmp->name);
271 } END_FOR_EACH_PTR(tmp);
272 free_slist(&slist);
275 static void match_return(struct statement *stmt)
277 char *name;
278 struct symbol *sym;
280 name = get_variable_from_expr_complex(stmt->ret_value, &sym);
281 if (sym)
282 assign_parent(sym);
283 free_string(name);
284 check_for_allocated();
287 static void set_new_true_false_paths(const char *name, struct symbol *sym)
289 struct smatch_state *tmp;
291 tmp = get_state(name, my_id, sym);
293 if (!tmp) {
294 return;
297 if (tmp == &isfree) {
298 smatch_msg("warn: why do you care about freed memory?");
301 if (tmp == &assigned) {
302 /* we don't care about assigned pointers any more */
303 return;
305 set_true_false_states(name, my_id, sym, &allocated, &isnull);
308 static void match_condition(struct expression *expr)
310 struct symbol *sym;
311 char *name;
313 expr = strip_expr(expr);
314 switch(expr->type) {
315 case EXPR_PREOP:
316 case EXPR_SYMBOL:
317 case EXPR_DEREF:
318 name = get_variable_from_expr_complex(expr, &sym);
319 if (!name)
320 return;
321 set_new_true_false_paths(name, sym);
322 free_string(name);
323 return;
324 case EXPR_ASSIGNMENT:
325 assign_seen++;
326 /* You have to deal with stuff like if (a = b = c) */
327 match_condition(expr->right);
328 match_condition(expr->left);
329 return;
330 default:
331 return;
335 static void match_function_call(struct expression *expr)
337 struct expression *tmp;
338 struct symbol *sym;
339 char *name;
340 char *fn_name;
341 struct sm_state *state;
343 fn_name = get_variable_from_expr(expr->fn, NULL);
345 if (fn_name && !strcmp(fn_name, "kfree")) {
346 match_kfree(expr);
349 FOR_EACH_PTR(expr->args, tmp) {
350 tmp = strip_expr(tmp);
351 name = get_variable_from_expr_complex(tmp, &sym);
352 if (!name)
353 continue;
354 if ((state = get_sm_state(name, my_id, sym))) {
355 if (possibly_allocated(state->possible)) {
356 set_state(name, my_id, sym, &assigned);
359 assign_parent(sym);
360 } END_FOR_EACH_PTR(tmp);
363 static void match_end_func(struct symbol *sym)
365 check_for_allocated();
366 free_trackers_and_list(&arguments);
369 void check_memory(int id)
371 my_id = id;
372 add_unmatched_state_hook(my_id, &unmatched_state);
373 add_hook(&match_function_def, FUNC_DEF_HOOK);
374 add_hook(&match_declarations, DECLARATION_HOOK);
375 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
376 add_hook(&match_condition, CONDITION_HOOK);
377 add_hook(&match_assign, ASSIGNMENT_HOOK);
378 add_hook(&match_return, RETURN_HOOK);
379 add_hook(&match_end_func, END_FUNC_HOOK);