err_ptr_deref: make err_ptr_min/max static
[smatch.git] / check_free.c
blobbdd545ca2bf280117690e4f002ba89925e6d93d3
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 void pre_merge_hook(struct sm_state *sm)
40 if (is_impossible_path())
41 set_state(my_id, sm->name, sm->sym, &ok);
44 static int is_freed(struct expression *expr)
46 struct sm_state *sm;
48 sm = get_sm_state_expr(my_id, expr);
49 if (sm && slist_has_state(sm->possible, &freed))
50 return 1;
51 return 0;
54 static void match_symbol(struct expression *expr)
56 char *name;
58 if (is_impossible_path())
59 return;
61 parent = expr_get_parent_expr(expr);
62 while (parent && parent->type == EXPR_PREOP && parent->op == '(')
63 parent = expr_get_parent_expr(parent);
64 if (parent && parent->type == EXPR_PREOP && parent->op == '&')
65 return;
67 if (!is_freed(expr))
68 return;
69 name = expr_to_var(expr);
70 sm_msg("warn: '%s' was already freed.", name);
71 free_string(name);
74 static void match_dereferences(struct expression *expr)
76 char *name;
78 if (expr->type != EXPR_PREOP)
79 return;
81 if (is_impossible_path())
82 return;
84 expr = strip_expr(expr->unop);
85 if (!is_freed(expr))
86 return;
87 name = expr_to_var(expr);
88 sm_msg("error: dereferencing freed memory '%s'", name);
89 set_state_expr(my_id, expr, &ok);
90 free_string(name);
93 static int ignored_params[16];
95 static void set_ignored_params(struct expression *call)
97 struct expression *arg;
98 const char *p;
99 int i;
101 memset(&ignored_params, 0, sizeof(ignored_params));
103 i = -1;
104 FOR_EACH_PTR(call->args, arg) {
105 i++;
106 if (arg->type != EXPR_STRING)
107 continue;
108 goto found;
109 } END_FOR_EACH_PTR(arg);
111 return;
113 found:
114 i++;
115 p = arg->string->data;
116 while ((p = strchr(p, '%'))) {
117 if (i >= ARRAY_SIZE(ignored_params))
118 return;
119 p++;
120 if (*p == '%') {
121 p++;
122 continue;
124 if (*p == '.')
125 p++;
126 if (*p == '*')
127 i++;
128 if (*p == 'p')
129 ignored_params[i] = 1;
130 i++;
134 static int is_free_func(struct expression *fn)
136 char *name;
137 int ret = 0;
139 name = expr_to_str(fn);
140 if (!name)
141 return 0;
142 if (strstr(name, "free"))
143 ret = 1;
144 free_string(name);
146 return ret;
149 static void match_call(struct expression *expr)
151 struct expression *arg;
152 char *name;
153 int i;
155 if (is_impossible_path())
156 return;
158 set_ignored_params(expr);
160 i = -1;
161 FOR_EACH_PTR(expr->args, arg) {
162 i++;
163 if (!is_pointer(arg))
164 continue;
165 if (!is_freed(arg))
166 continue;
167 if (ignored_params[i])
168 continue;
170 name = expr_to_var(arg);
171 if (is_free_func(expr->fn))
172 sm_msg("error: double free of '%s'", name);
173 else
174 sm_msg("warn: passing freed memory '%s'", name);
175 set_state_expr(my_id, arg, &ok);
176 free_string(name);
177 } END_FOR_EACH_PTR(arg);
180 static void match_return(struct expression *expr)
182 char *name;
184 if (is_impossible_path())
185 return;
187 if (!expr)
188 return;
189 if (!is_freed(expr))
190 return;
192 name = expr_to_var(expr);
193 sm_msg("warn: returning freed memory '%s'", name);
194 set_state_expr(my_id, expr, &ok);
195 free_string(name);
198 static void match_free(const char *fn, struct expression *expr, void *param)
200 struct expression *arg;
202 if (is_impossible_path())
203 return;
205 arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
206 if (!arg)
207 return;
208 if (is_freed(arg)) {
209 char *name = expr_to_var(arg);
211 sm_msg("error: double free of '%s'", name);
212 free_string(name);
214 set_state_expr(my_id, arg, &freed);
217 static void set_param_freed(struct expression *arg, char *key, char *unused)
219 struct symbol *sym;
220 char *name;
222 name = get_variable_from_key(arg, key, &sym);
223 if (!name || !sym)
224 goto free;
226 set_state(my_id, name, sym, &freed);
227 free:
228 free_string(name);
231 int parent_is_free_var_sym(const char *name, struct symbol *sym)
233 char buf[256];
234 char *start;
235 char *end;
236 struct smatch_state *state;
238 strncpy(buf, name, sizeof(buf) - 1);
239 buf[sizeof(buf) - 1] = '\0';
241 start = &buf[0];
242 while ((*start == '&'))
243 start++;
245 while ((end = strrchr(start, '-'))) {
246 *end = '\0';
247 state = get_state(my_id, start, sym);
248 if (state == &freed)
249 return 1;
251 return 0;
254 int parent_is_free(struct expression *expr)
256 struct symbol *sym;
257 char *var;
258 int ret = 0;
260 expr = strip_expr(expr);
261 var = expr_to_var_sym(expr, &sym);
262 if (!var || !sym)
263 goto free;
264 ret = parent_is_free_var_sym(var, sym);
265 free:
266 free_string(var);
267 return ret;
270 void check_free(int id)
272 my_id = id;
274 if (option_project == PROJ_KERNEL) {
275 add_function_hook("kfree", &match_free, INT_PTR(0));
276 add_function_hook("kmem_cache_free", &match_free, INT_PTR(1));
277 } else {
278 add_function_hook("free", &match_free, INT_PTR(0));
281 if (option_spammy)
282 add_hook(&match_symbol, SYM_HOOK);
283 add_hook(&match_dereferences, DEREF_HOOK);
284 add_hook(&match_call, FUNCTION_CALL_HOOK);
285 add_hook(&match_return, RETURN_HOOK);
287 add_modification_hook(my_id, &ok_to_use);
288 select_call_implies_hook(PARAM_FREED, &set_param_freed);
289 add_pre_merge_hook(my_id, &pre_merge_hook);