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
42static int my_id;
43
44STATE(allocated);
45STATE(ok);
46
47static void set_parent(struct expression *expr, struct smatch_state *state);
48
49static const char *allocation_funcs[] = {
50	"malloc",
51	"kmalloc",
52	"kzalloc",
53	"kmemdup",
54};
55
56static 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
68static 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
83static 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;
95out:
96	free_string(name);
97	return ret;
98}
99
100static 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);
116out:
117	free_string(name);
118	return ret;
119
120}
121
122static 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
133static 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
156static 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);
177free:
178	free_string(name);
179}
180
181static 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
190static 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
213static 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
226static 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
239static 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
247static void match_end_func(struct symbol *sym)
248{
249	if (__inline_fn)
250		return;
251	check_for_allocated();
252}
253
254void 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