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  * The point of this check is to look for leaks.
20  * foo = malloc();  // <- mark it as allocated.
21  * A variable becomes &ok if we:
22  * 1) assign it to another variable.
23  * 2) pass it to a function.
24  *
25  * One complication is dealing with stuff like:
26  * foo->bar = malloc();
27  * foo->baz = malloc();
28  * foo = something();
29  *
30  * The work around is that for now what this check only
31  * checks simple expressions and doesn't check whether
32  * foo->bar is leaked.
33  *
34  */
35 
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include "parse.h"
39 #include "smatch.h"
40 #include "smatch_slist.h"
41 
42 static int my_id;
43 
44 STATE(allocated);
45 STATE(ok);
46 
47 static void set_parent(struct expression *expr, struct smatch_state *state);
48 
49 static const char *allocation_funcs[] = {
50 	"malloc",
51 	"kmalloc",
52 	"kzalloc",
53 	"kmemdup",
54 };
55 
alloc_parent_str(struct symbol * sym)56 static char *alloc_parent_str(struct symbol *sym)
57 {
58 	static char buf[256];
59 
60 	if (!sym || !sym->ident)
61 		return NULL;
62 
63 	snprintf(buf, 255, "%s", sym->ident->name);
64 	buf[255] = '\0';
65 	return alloc_string(buf);
66 }
67 
get_parent_from_expr(struct expression * expr,struct symbol ** sym)68 static char *get_parent_from_expr(struct expression *expr, struct symbol **sym)
69 {
70 	char *name;
71 
72 	expr = strip_expr(expr);
73 
74 	name = expr_to_str_sym(expr, sym);
75 	free_string(name);
76 	if (!name || !*sym || !(*sym)->ident) {
77 		*sym = NULL;
78 		return NULL;
79 	}
80 	return alloc_parent_str(*sym);
81 }
82 
is_local(struct expression * expr)83 static int is_local(struct expression *expr)
84 {
85 	char *name;
86 	struct symbol *sym;
87 	int ret = 0;
88 
89 	name = expr_to_str_sym(expr, &sym);
90 	if (!name || !sym)
91 		goto out;
92 	if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
93 		goto out;
94 	ret = 1;
95 out:
96 	free_string(name);
97 	return ret;
98 }
99 
is_param(struct expression * expr)100 static int is_param(struct expression *expr)
101 {
102 	char *name;
103 	struct symbol *sym;
104 	struct symbol *tmp;
105 	int ret = 0;
106 
107 	name = expr_to_str_sym(expr, &sym);
108 	if (!name || !sym)
109 		goto out;
110 	FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, tmp) {
111 		if (tmp == sym) {
112 			ret = 1;
113 			goto out;
114 		}
115 	} END_FOR_EACH_PTR(tmp);
116 out:
117 	free_string(name);
118 	return ret;
119 
120 }
121 
match_alloc(const char * fn,struct expression * expr,void * unused)122 static void match_alloc(const char *fn, struct expression *expr, void *unused)
123 {
124 	if (!is_local(expr->left))
125 		return;
126 	if (is_param(expr->left))
127 		return;
128 	if (expr->left->type != EXPR_SYMBOL)
129 		return;
130 	set_state_expr(my_id, expr->left, &allocated);
131 }
132 
match_condition(struct expression * expr)133 static void match_condition(struct expression *expr)
134 {
135 	struct sm_state *sm;
136 
137 	expr = strip_expr(expr);
138 
139 	switch (expr->type) {
140 	case EXPR_PREOP:
141 	case EXPR_SYMBOL:
142 	case EXPR_DEREF:
143 		sm = get_sm_state_expr(my_id, expr);
144 		if (sm && slist_has_state(sm->possible, &allocated))
145 			set_true_false_states_expr(my_id, expr, NULL, &ok);
146 		return;
147 	case EXPR_ASSIGNMENT:
148 		 /* You have to deal with stuff like if (a = b = c) */
149 		match_condition(expr->left);
150 		return;
151 	default:
152 		return;
153 	}
154 }
155 
set_parent(struct expression * expr,struct smatch_state * state)156 static void set_parent(struct expression *expr, struct smatch_state *state)
157 {
158 	char *name;
159 	struct symbol *sym;
160 
161 	expr = strip_expr(expr);
162 	if (!expr)
163 		return;
164 	if (expr->type == EXPR_CONDITIONAL ||
165 	    expr->type == EXPR_SELECT) {
166 		set_parent(expr->cond_true, state);
167 		set_parent(expr->cond_false, state);
168 		return;
169 	}
170 
171 	name = get_parent_from_expr(expr, &sym);
172 	if (!name || !sym)
173 		goto free;
174 	if (state == &ok && !get_state(my_id, name, sym))
175 		goto free;
176 	set_state(my_id, name, sym, state);
177 free:
178 	free_string(name);
179 }
180 
match_function_call(struct expression * expr)181 static void match_function_call(struct expression *expr)
182 {
183 	struct expression *tmp;
184 
185 	FOR_EACH_PTR(expr->args, tmp) {
186 		set_parent(tmp, &ok);
187 	} END_FOR_EACH_PTR(tmp);
188 }
189 
warn_if_allocated(struct expression * expr)190 static void warn_if_allocated(struct expression *expr)
191 {
192 	struct sm_state *sm;
193 	char *name;
194 	sval_t sval;
195 
196 	if (get_implied_value(expr, &sval) && sval.value == 0)
197 		return;
198 
199 	sm = get_sm_state_expr(my_id, expr);
200 	if (!sm)
201 		return;
202 	if (!slist_has_state(sm->possible, &allocated))
203 		return;
204 
205 	name = expr_to_var(expr);
206 	sm_warning("overwrite may leak '%s'", name);
207 	free_string(name);
208 
209 	/* silence further warnings */
210 	set_state_expr(my_id, expr, &ok);
211 }
212 
match_assign(struct expression * expr)213 static void match_assign(struct expression *expr)
214 {
215 	struct expression *right;
216 
217 	right = expr->right;
218 
219 	while (right->type == EXPR_ASSIGNMENT)
220 		right = right->left;
221 
222 	warn_if_allocated(expr->left);
223 	set_parent(right, &ok);
224 }
225 
check_for_allocated(void)226 static void check_for_allocated(void)
227 {
228 	struct stree *stree;
229 	struct sm_state *tmp;
230 
231 	stree = __get_cur_stree();
232 	FOR_EACH_MY_SM(my_id, stree, tmp) {
233 		if (!slist_has_state(tmp->possible, &allocated))
234 			continue;
235 		sm_warning("possible memory leak of '%s'", tmp->name);
236 	} END_FOR_EACH_SM(tmp);
237 }
238 
match_return(struct expression * ret_value)239 static void match_return(struct expression *ret_value)
240 {
241 	if (__inline_fn)
242 		return;
243 	set_parent(ret_value, &ok);
244 	check_for_allocated();
245 }
246 
match_end_func(struct symbol * sym)247 static void match_end_func(struct symbol *sym)
248 {
249 	if (__inline_fn)
250 		return;
251 	check_for_allocated();
252 }
253 
check_leaks(int id)254 void check_leaks(int id)
255 {
256 	int i;
257 
258 	my_id = id;
259 
260 	for (i = 0; i < ARRAY_SIZE(allocation_funcs); i++)
261 		add_function_assign_hook(allocation_funcs[i], &match_alloc, NULL);
262 
263 	add_hook(&match_condition, CONDITION_HOOK);
264 
265 	add_hook(&match_function_call, FUNCTION_CALL_HOOK);
266 	add_hook(&match_assign, ASSIGNMENT_HOOK);
267 
268 	add_hook(&match_return, RETURN_HOOK);
269 	add_hook(&match_end_func, END_FUNC_HOOK);
270 }
271