1 /*
2  * Copyright (C) 2010 Dan Carpenter.
3  *
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.
8  *
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.
13  *
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
16  */
17 
18 /*
19  * check_memory() is getting too big and messy.
20  *
21  */
22 
23 #include <string.h>
24 #include "smatch.h"
25 #include "smatch_slist.h"
26 #include "smatch_extra.h"
27 
28 static int my_id;
29 
30 STATE(freed);
31 STATE(ok);
32 
ok_to_use(struct sm_state * sm,struct expression * mod_expr)33 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
34 {
35 	if (sm->state != &ok)
36 		set_state(my_id, sm->name, sm->sym, &ok);
37 }
38 
pre_merge_hook(struct sm_state * cur,struct sm_state * other)39 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
40 {
41 	if (is_impossible_path())
42 		set_state(my_id, cur->name, cur->sym, &ok);
43 }
44 
is_freed(struct expression * expr)45 static int is_freed(struct expression *expr)
46 {
47 	struct sm_state *sm;
48 
49 	sm = get_sm_state_expr(my_id, expr);
50 	if (sm && slist_has_state(sm->possible, &freed))
51 		return 1;
52 	return 0;
53 }
54 
match_symbol(struct expression * expr)55 static void match_symbol(struct expression *expr)
56 {
57 	struct expression *parent;
58 	char *name;
59 
60 	if (is_impossible_path())
61 		return;
62 	if (__in_fake_parameter_assign)
63 		return;
64 
65 	parent = expr_get_parent_expr(expr);
66 	while (parent && parent->type == EXPR_PREOP && parent->op == '(')
67 		parent = expr_get_parent_expr(parent);
68 	if (parent && parent->type == EXPR_PREOP && parent->op == '&')
69 		return;
70 
71 	if (!is_freed(expr))
72 		return;
73 	name = expr_to_var(expr);
74 	sm_warning("'%s' was already freed.", name);
75 	free_string(name);
76 }
77 
match_dereferences(struct expression * expr)78 static void match_dereferences(struct expression *expr)
79 {
80 	char *name;
81 
82 	if (__in_fake_parameter_assign)
83 		return;
84 
85 	if (expr->type != EXPR_PREOP)
86 		return;
87 
88 	if (is_impossible_path())
89 		return;
90 
91 	expr = strip_expr(expr->unop);
92 	if (!is_freed(expr))
93 		return;
94 	name = expr_to_var(expr);
95 	sm_error("dereferencing freed memory '%s'", name);
96 	set_state_expr(my_id, expr, &ok);
97 	free_string(name);
98 }
99 
100 static int ignored_params[16];
101 
set_ignored_params(struct expression * call)102 static void set_ignored_params(struct expression *call)
103 {
104 	struct expression *arg;
105 	const char *p;
106 	int i;
107 
108 	memset(&ignored_params, 0, sizeof(ignored_params));
109 
110 	i = -1;
111 	FOR_EACH_PTR(call->args, arg) {
112 		i++;
113 		if (arg->type != EXPR_STRING)
114 			continue;
115 		goto found;
116 	} END_FOR_EACH_PTR(arg);
117 
118 	return;
119 
120 found:
121 	i++;
122 	p = arg->string->data;
123 	while ((p = strchr(p, '%'))) {
124 		if (i >= ARRAY_SIZE(ignored_params))
125 			return;
126 		p++;
127 		if (*p == '%') {
128 			p++;
129 			continue;
130 		}
131 		if (*p == '.')
132 			p++;
133 		if (*p == '*')
134 			i++;
135 		if (*p == 'p')
136 			ignored_params[i] = 1;
137 		i++;
138 	}
139 }
140 
is_free_func(struct expression * fn)141 static int is_free_func(struct expression *fn)
142 {
143 	char *name;
144 	int ret = 0;
145 
146 	name = expr_to_str(fn);
147 	if (!name)
148 		return 0;
149 	if (strstr(name, "free"))
150 		ret = 1;
151 	free_string(name);
152 
153 	return ret;
154 }
155 
match_call(struct expression * expr)156 static void match_call(struct expression *expr)
157 {
158 	struct expression *arg;
159 	char *name;
160 	int i;
161 
162 	if (is_impossible_path())
163 		return;
164 
165 	set_ignored_params(expr);
166 
167 	i = -1;
168 	FOR_EACH_PTR(expr->args, arg) {
169 		i++;
170 		if (!is_pointer(arg))
171 			continue;
172 		if (!is_freed(arg))
173 			continue;
174 		if (ignored_params[i])
175 			continue;
176 
177 		name = expr_to_var(arg);
178 		if (is_free_func(expr->fn))
179 			sm_error("double free of '%s'", name);
180 		else
181 			sm_warning("passing freed memory '%s'", name);
182 		set_state_expr(my_id, arg, &ok);
183 		free_string(name);
184 	} END_FOR_EACH_PTR(arg);
185 }
186 
match_return(struct expression * expr)187 static void match_return(struct expression *expr)
188 {
189 	char *name;
190 
191 	if (is_impossible_path())
192 		return;
193 	if (__in_fake_parameter_assign)
194 		return;
195 
196 	if (!expr)
197 		return;
198 	if (!is_freed(expr))
199 		return;
200 
201 	name = expr_to_var(expr);
202 	sm_warning("returning freed memory '%s'", name);
203 	set_state_expr(my_id, expr, &ok);
204 	free_string(name);
205 }
206 
match_free(const char * fn,struct expression * expr,void * param)207 static void match_free(const char *fn, struct expression *expr, void *param)
208 {
209 	struct expression *arg;
210 
211 	if (is_impossible_path())
212 		return;
213 
214 	arg = get_argument_from_call_expr(expr->args, PTR_INT(param));
215 	if (!arg)
216 		return;
217 	if (is_freed(arg)) {
218 		char *name = expr_to_var(arg);
219 
220 		sm_error("double free of '%s'", name);
221 		free_string(name);
222 	}
223 	set_state_expr(my_id, arg, &freed);
224 }
225 
set_param_freed(struct expression * call,struct expression * arg,char * key,char * unused)226 static void set_param_freed(struct expression *call, struct expression *arg, char *key, char *unused)
227 {
228 	struct symbol *sym;
229 	char *name;
230 
231 	name = get_variable_from_key(arg, key, &sym);
232 	if (!name || !sym)
233 		goto free;
234 
235 	set_state(my_id, name, sym, &freed);
236 free:
237 	free_string(name);
238 }
239 
parent_is_free_var_sym(const char * name,struct symbol * sym)240 int parent_is_free_var_sym(const char *name, struct symbol *sym)
241 {
242 	char buf[256];
243 	char *start;
244 	char *end;
245 	struct smatch_state *state;
246 
247 	if (option_project == PROJ_KERNEL)
248 		return parent_is_free_var_sym_strict(name, sym);
249 
250 	strncpy(buf, name, sizeof(buf) - 1);
251 	buf[sizeof(buf) - 1] = '\0';
252 
253 	start = &buf[0];
254 	while ((*start == '&'))
255 		start++;
256 
257 	while ((end = strrchr(start, '-'))) {
258 		*end = '\0';
259 		state = __get_state(my_id, start, sym);
260 		if (state == &freed)
261 			return 1;
262 	}
263 	return 0;
264 }
265 
parent_is_free(struct expression * expr)266 int parent_is_free(struct expression *expr)
267 {
268 	struct symbol *sym;
269 	char *var;
270 	int ret = 0;
271 
272 	expr = strip_expr(expr);
273 	var = expr_to_var_sym(expr, &sym);
274 	if (!var || !sym)
275 		goto free;
276 	ret = parent_is_free_var_sym(var, sym);
277 free:
278 	free_string(var);
279 	return ret;
280 }
281 
check_free(int id)282 void check_free(int id)
283 {
284 	my_id = id;
285 
286 	if (option_project == PROJ_KERNEL) {
287 		/* The kernel use check_free_strict.c */
288 		return;
289 	}
290 
291 	add_function_hook("free", &match_free, INT_PTR(0));
292 
293 	if (option_spammy)
294 		add_hook(&match_symbol, SYM_HOOK);
295 	add_hook(&match_dereferences, DEREF_HOOK);
296 	add_hook(&match_call, FUNCTION_CALL_HOOK);
297 	add_hook(&match_return, RETURN_HOOK);
298 
299 	add_modification_hook(my_id, &ok_to_use);
300 	select_return_implies_hook(PARAM_FREED, &set_param_freed);
301 	add_pre_merge_hook(my_id, &pre_merge_hook);
302 }
303