comparison: handle preops like "if (++a == b)"
[smatch.git] / check_free.c
blobea9e5c7d212f5d9c7f2e19d12eed589f2de02088
1 /*
2 * Copyright (C) 2010 Dan Carpenter.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
19 * check_memory() is getting too big and messy.
23 #include <string.h>
24 #include "smatch.h"
25 #include "smatch_slist.h"
27 static int my_id;
29 STATE(freed);
30 STATE(ok);
32 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
34 if (sm->state != &ok)
35 set_state(my_id, sm->name, sm->sym, &ok);
38 static int is_freed(struct expression *expr)
40 struct sm_state *sm;
42 sm = get_sm_state_expr(my_id, expr);
43 if (sm && slist_has_state(sm->possible, &freed))
44 return 1;
45 return 0;
48 static void match_symbol(struct expression *expr)
50 char *name;
52 if (!is_freed(expr))
53 return;
54 name = expr_to_var(expr);
55 sm_msg("warn: '%s' was already freed.", name);
56 free_string(name);
59 static void match_dereferences(struct expression *expr)
61 char *name;
63 if (expr->type != EXPR_PREOP)
64 return;
65 expr = strip_expr(expr->unop);
67 if (!is_freed(expr))
68 return;
69 name = expr_to_var(expr);
70 sm_msg("error: dereferencing freed memory '%s'", name);
71 set_state_expr(my_id, expr, &ok);
72 free_string(name);
75 static int ignored_params[16];
77 static void set_ignored_params(struct expression *call)
79 struct expression *arg;
80 const char *p;
81 int i;
83 memset(&ignored_params, 0, sizeof(ignored_params));
85 i = -1;
86 FOR_EACH_PTR(call->args, arg) {
87 i++;
88 if (arg->type != EXPR_STRING)
89 continue;
90 goto found;
91 } END_FOR_EACH_PTR(arg);
93 return;
95 found:
96 i++;
97 p = arg->string->data;
98 while ((p = strchr(p, '%'))) {
99 if (i >= ARRAY_SIZE(ignored_params))
100 return;
101 p++;
102 if (*p == '%') {
103 p++;
104 continue;
106 if (*p == '.')
107 p++;
108 if (*p == '*')
109 i++;
110 if (*p == 'p')
111 ignored_params[i] = 1;
112 i++;
116 static int is_free_func(struct expression *fn)
118 char *name;
119 int ret = 0;
121 name = expr_to_str(fn);
122 if (!name)
123 return 0;
124 if (strstr(name, "free"))
125 ret = 1;
126 free_string(name);
128 return ret;
131 static void match_call(struct expression *expr)
133 struct expression *arg;
134 char *name;
135 int i;
137 set_ignored_params(expr);
139 i = -1;
140 FOR_EACH_PTR(expr->args, arg) {
141 i++;
142 if (!is_pointer(arg))
143 continue;
144 if (!is_freed(arg))
145 continue;
146 if (ignored_params[i])
147 continue;
149 name = expr_to_var(arg);
150 if (is_free_func(expr->fn))
151 sm_msg("error: double free of '%s'", name);
152 else
153 sm_msg("warn: passing freed memory '%s'", name);
154 set_state_expr(my_id, arg, &ok);
155 free_string(name);
156 } END_FOR_EACH_PTR(arg);
159 static void match_return(struct expression *expr)
161 char *name;
163 if (!expr)
164 return;
165 if (!is_freed(expr))
166 return;
168 name = expr_to_var(expr);
169 sm_msg("warn: returning freed memory '%s'", name);
170 set_state_expr(my_id, expr, &ok);
171 free_string(name);
174 static void match_free(const char *fn, struct expression *expr, void *param)
176 struct expression *arg;
178 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
179 if (!arg)
180 return;
181 if (is_freed(arg)) {
182 char *name = expr_to_var(arg);
184 sm_msg("error: double free of '%s'", name);
185 free_string(name);
187 set_state_expr(my_id, arg, &freed);
190 static void set_param_freed(struct expression *arg, char *key, char *unused)
192 struct symbol *sym;
193 char *name;
195 name = get_variable_from_key(arg, key, &sym);
196 if (!name || !sym)
197 goto free;
199 set_state(my_id, name, sym, &freed);
200 free:
201 free_string(name);
204 int parent_is_free_var_sym(const char *name, struct symbol *sym)
206 char buf[256];
207 char *start;
208 char *end;
209 struct smatch_state *state;
211 strncpy(buf, name, sizeof(buf) - 1);
212 buf[sizeof(buf) - 1] = '\0';
214 start = &buf[0];
215 while ((*start == '&'))
216 start++;
218 while ((end = strrchr(start, '-'))) {
219 *end = '\0';
220 state = get_state(my_id, start, sym);
221 if (state == &freed)
222 return 1;
224 return 0;
227 int parent_is_free(struct expression *expr)
229 struct symbol *sym;
230 char *var;
231 int ret = 0;
233 expr = strip_expr(expr);
234 var = expr_to_var_sym(expr, &sym);
235 if (!var || !sym)
236 goto free;
237 ret = parent_is_free_var_sym(var, sym);
238 free:
239 free_string(var);
240 return ret;
243 void check_free(int id)
245 my_id = id;
247 if (option_project == PROJ_KERNEL) {
248 add_function_hook("kfree", &match_free, INT_PTR(0));
249 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
250 } else {
251 add_function_hook("free", &match_free, INT_PTR(0));
254 if (option_spammy)
255 add_hook(&match_symbol, SYM_HOOK);
256 add_hook(&match_dereferences, DEREF_HOOK);
257 add_hook(&match_call, FUNCTION_CALL_HOOK);
258 add_hook(&match_return, RETURN_HOOK);
260 add_modification_hook(my_id, &ok_to_use);
261 select_call_implies_hook(PARAM_FREED, &set_param_freed);