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