Fix segfault when handling implicitly declared functions.
[smatch.git] / check_memory.c
blobc97c94be3925e0375c9f7a9b6582a5fecc63d666
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(returned);
20 STATE(unfree);
22 static void match_assign(struct expression *expr)
24 struct expression *left, *right;
25 char *left_name, *right_name;
26 struct symbol *left_sym, *right_sym;
27 struct smatch_state *state;
29 left = strip_expr(expr->left);
30 left_name = get_variable_from_expr(left, &left_sym);
31 if (!left_name)
32 return;
33 if (!left_sym) {
34 free_string(left_name);
35 return;
38 right = strip_expr(expr->right);
40 right_name = NULL;
41 if (right->type == EXPR_CALL)
42 right_name = get_variable_from_expr(expr->fn, NULL);
43 if (right_name && !strcmp(right_name, "kmalloc")) {
44 if (left_sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
45 return;
46 set_state(left_name, my_id, left_sym, &allocated);
47 free_string(right_name);
48 return;
50 free_string(right_name);
52 right_name = get_variable_from_expr(right, &right_sym);
53 if (right_name && (state = get_state(right_name, my_id, right_sym))) {
54 if (state == &isfree)
55 smatch_msg("assigning freed pointer");
56 set_state(right_name, my_id, right_sym, &assigned);
57 free_string(right_name);
58 return;
60 free_string(right_name);
62 if (is_zero(right))
63 return;
65 if (get_state(left_name, my_id, left_sym) == &isfree) {
66 set_state(left_name, my_id, left_sym, &unfree);
70 static int is_null(char *name, struct symbol *sym)
72 struct smatch_state *state;
74 /*
75 * FIXME. Ha ha ha... This is so wrong.
76 * I'm pulling in data from the check_null_deref script.
77 * I just happen to know that its ID is 3.
78 * The correct approved way to do this is to get the data from
79 * smatch_extra. But right now smatch_extra doesn't track it.
81 state = get_state(name, 3, sym);
82 if (state && !strcmp(state->name, "isnull"))
83 return 1;
84 return 0;
87 static void match_kfree(struct expression *expr)
89 struct expression *ptr_expr;
90 struct state_list *slist;
91 char *fn_name;
92 char *ptr_name;
93 struct symbol *ptr_sym;
96 fn_name = get_variable_from_expr(expr->fn, NULL);
98 if (!fn_name || strcmp(fn_name, "kfree"))
99 return;
101 ptr_expr = get_argument_from_call_expr(expr->args, 0);
102 ptr_name = get_variable_from_expr(ptr_expr, &ptr_sym);
103 slist = get_possible_states(ptr_name, my_id, ptr_sym);
104 if (slist_has_state(slist, &isfree) && !is_null(ptr_name, ptr_sym)) {
105 smatch_msg("double free of %s", ptr_name);
107 set_state(ptr_name, my_id, ptr_sym, &isfree);
109 free_string(fn_name);
112 static int possibly_allocated(struct state_list *slist)
114 struct sm_state *tmp;
116 FOR_EACH_PTR(slist, tmp) {
117 if (tmp->state == &allocated)
118 return 1;
119 } END_FOR_EACH_PTR(tmp);
120 return 0;
123 static void check_for_allocated()
125 struct state_list *slist;
126 struct sm_state *tmp;
128 slist = get_all_states(my_id);
129 FOR_EACH_PTR(slist, tmp) {
130 if (possibly_allocated(tmp->possible) &&
131 !is_null(tmp->name, tmp->sym))
132 smatch_msg("possible memery leak of %s", tmp->name);
133 } END_FOR_EACH_PTR(tmp);
137 static void match_return(struct statement *stmt)
139 char *name;
140 struct symbol *sym;
141 struct smatch_state *state;
143 name = get_variable_from_expr(stmt->ret_value, &sym);
144 if ((state = get_state(name, my_id, sym))) {
145 set_state(name, my_id, sym, &returned);
148 check_for_allocated();
151 static void match_end_func(struct symbol *sym)
153 check_for_allocated();
156 void register_memory(int id)
158 my_id = id;
159 add_hook(&match_kfree, FUNCTION_CALL_HOOK);
160 add_hook(&match_assign, ASSIGNMENT_HOOK);
161 add_hook(&match_return, RETURN_HOOK);
162 add_hook(&match_end_func, END_FUNC_HOOK);