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