Print this page
smatch: check libld_* allocation functions
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/smatch/src/check_memory.c
+++ new/usr/src/tools/smatch/src/check_memory.c
1 1 /*
2 2 * Copyright (C) 2008 Dan Carpenter.
3 3 *
4 4 * This program is free software; you can redistribute it and/or
5 5 * modify it under the terms of the GNU General Public License
6 6 * as published by the Free Software Foundation; either version 2
7 7 * of the License, or (at your option) any later version.
8 8 *
9 9 * This program is distributed in the hope that it will be useful,
10 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 * GNU General Public License for more details.
13 13 *
14 14 * You should have received a copy of the GNU General Public License
15 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16 16 */
17 17
18 18 #include <fcntl.h>
19 19 #include <unistd.h>
20 20 #include "parse.h"
21 21 #include "smatch.h"
22 22 #include "smatch_slist.h"
23 23
24 24 static void check_sm_is_leaked(struct sm_state *sm);
25 25
26 26 static int my_id;
27 27
28 28 STATE(allocated);
29 29 STATE(assigned);
30 30 STATE(isfree);
31 31 STATE(malloced);
32 32 STATE(isnull);
33 33 STATE(unfree);
34 34
35 35 /*
36 36 malloced --> allocated --> assigned --> isfree +
37 37 \-> isnull. \-> isfree +
38 38
39 39 isfree --> unfree.
40 40 \-> isnull.
41 41 */
42 42
43 43 static struct tracker_list *arguments;
44 44
45 45 static const char *allocation_funcs[] = {
46 46 "malloc",
47 47 "kmalloc",
48 48 "kzalloc",
49 49 NULL,
50 50 };
51 51
52 52 static char *get_parent_name(struct symbol *sym)
53 53 {
54 54 static char buf[256];
55 55
56 56 if (!sym || !sym->ident)
57 57 return NULL;
58 58
59 59 snprintf(buf, 255, "-%s", sym->ident->name);
60 60 buf[255] = '\0';
61 61 return alloc_string(buf);
62 62 }
63 63
64 64 static int is_parent_sym(const char *name)
65 65 {
66 66 if (!strncmp(name, "-", 1))
67 67 return 1;
68 68 return 0;
69 69 }
70 70
71 71 static int is_complex(struct expression *expr)
72 72 {
73 73 char *name;
74 74 int ret = 1;
75 75
76 76 name = expr_to_var(expr);
77 77 if (name)
78 78 ret = 0;
79 79 free_string(name);
80 80 return ret;
81 81 }
82 82
83 83 static struct smatch_state *unmatched_state(struct sm_state *sm)
84 84 {
85 85 if (is_parent_sym(sm->name))
86 86 return &assigned;
87 87 return &undefined;
88 88 }
89 89
90 90 static void assign_parent(struct symbol *sym)
91 91 {
92 92 char *name;
93 93
94 94 name = get_parent_name(sym);
95 95 if (!name)
96 96 return;
97 97 set_state(my_id, name, sym, &assigned);
98 98 free_string(name);
99 99 }
100 100
101 101 static int parent_is_assigned(struct symbol *sym)
102 102 {
103 103 struct smatch_state *state;
104 104 char *name;
105 105
106 106 name = get_parent_name(sym);
107 107 if (!name)
108 108 return 0;
109 109 state = get_state(my_id, name, sym);
110 110 free_string(name);
111 111 if (state == &assigned)
112 112 return 1;
113 113 return 0;
114 114 }
115 115
116 116 static int is_allocation(struct expression *expr)
117 117 {
118 118 char *fn_name;
119 119 int i;
120 120
121 121 if (expr->type != EXPR_CALL)
122 122 return 0;
123 123
124 124 if (!(fn_name = expr_to_var_sym(expr->fn, NULL)))
125 125 return 0;
126 126
127 127 for (i = 0; allocation_funcs[i]; i++) {
128 128 if (!strcmp(fn_name, allocation_funcs[i])) {
129 129 free_string(fn_name);
130 130 return 1;
131 131 }
132 132 }
133 133 free_string(fn_name);
134 134 return 0;
135 135 }
136 136
137 137 static int is_freed(const char *name, struct symbol *sym)
138 138 {
139 139 struct state_list *slist;
140 140
141 141 slist = get_possible_states(my_id, name, sym);
142 142 if (slist_has_state(slist, &isfree)) {
143 143 return 1;
144 144 }
145 145 return 0;
146 146 }
147 147
148 148 static int is_argument(struct symbol *sym)
149 149 {
150 150 struct tracker *arg;
151 151
152 152 FOR_EACH_PTR(arguments, arg) {
153 153 if (arg->sym == sym)
154 154 return 1;
155 155 } END_FOR_EACH_PTR(arg);
156 156 return 0;
157 157 }
158 158
159 159 static void match_function_def(struct symbol *sym)
160 160 {
161 161 struct symbol *arg;
162 162
163 163 FOR_EACH_PTR(sym->ctype.base_type->arguments, arg) {
164 164 add_tracker(&arguments, my_id, (arg->ident?arg->ident->name:"NULL"), arg);
165 165 } END_FOR_EACH_PTR(arg);
166 166 }
167 167
168 168 static int is_parent(struct expression *expr)
169 169 {
170 170 if (expr->type == EXPR_DEREF)
171 171 return 0;
172 172 return 1;
173 173 }
174 174
175 175 static void match_assign(struct expression *expr)
176 176 {
177 177 struct expression *left, *right;
178 178 char *left_name = NULL;
179 179 char *right_name = NULL;
180 180 struct symbol *left_sym, *right_sym;
181 181 struct smatch_state *state;
182 182 struct state_list *slist;
183 183 struct sm_state *tmp;
184 184
185 185 left = strip_expr(expr->left);
186 186 left_name = expr_to_str_sym(left, &left_sym);
187 187
188 188 right = strip_expr(expr->right);
189 189 while (right->type == EXPR_ASSIGNMENT)
190 190 right = right->left;
191 191
192 192 if (left_name && left_sym && is_allocation(right) &&
193 193 !(left_sym->ctype.modifiers &
194 194 (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE)) &&
195 195 !parent_is_assigned(left_sym)) {
196 196 set_state(my_id, left_name, left_sym, &malloced);
197 197 goto exit;
198 198 }
199 199
200 200 right_name = expr_to_str_sym(right, &right_sym);
201 201
202 202 if (right_name && (state = get_state(my_id, right_name, right_sym))) {
203 203 if (state == &isfree && !is_complex(right))
204 204 sm_error("assigning freed pointer '%s'", right_name);
205 205 set_state(my_id, right_name, right_sym, &assigned);
206 206 }
207 207
208 208 if (is_zero(expr->right)) {
209 209 slist = get_possible_states(my_id, left_name, left_sym);
210 210
211 211 FOR_EACH_PTR(slist, tmp) {
212 212 check_sm_is_leaked(tmp);
213 213 } END_FOR_EACH_PTR(tmp);
214 214 }
215 215
216 216 if (is_freed(left_name, left_sym)) {
217 217 set_state(my_id, left_name, left_sym, &unfree);
218 218 }
219 219 if (left_name && is_parent(left))
220 220 assign_parent(left_sym);
221 221 if (right_name && is_parent(right))
222 222 assign_parent(right_sym);
223 223 exit:
224 224 free_string(left_name);
225 225 free_string(right_name);
226 226 }
227 227
228 228 static int is_null(const char *name, struct symbol *sym)
229 229 {
230 230 struct smatch_state *state;
231 231
232 232 state = get_state(my_id, name, sym);
233 233 if (state && !strcmp(state->name, "isnull"))
234 234 return 1;
235 235 return 0;
236 236 }
237 237
238 238 static void set_unfree(struct sm_state *sm, struct expression *mod_expr)
239 239 {
240 240 if (slist_has_state(sm->possible, &isfree))
241 241 set_state(my_id, sm->name, sm->sym, &unfree);
242 242 }
243 243
244 244 static void match_free_func(const char *fn, struct expression *expr, void *data)
245 245 {
246 246 struct expression *ptr_expr;
247 247 char *ptr_name;
248 248 struct symbol *ptr_sym;
249 249 int arg_num = PTR_INT(data);
250 250
251 251 ptr_expr = get_argument_from_call_expr(expr->args, arg_num);
252 252 ptr_name = expr_to_var_sym(ptr_expr, &ptr_sym);
253 253 if (!ptr_name)
254 254 return;
255 255 set_state(my_id, ptr_name, ptr_sym, &isfree);
256 256 free_string(ptr_name);
257 257 }
258 258
259 259 static int possibly_allocated(struct state_list *slist)
260 260 {
261 261 struct sm_state *tmp;
262 262
263 263 FOR_EACH_PTR(slist, tmp) {
264 264 if (tmp->state == &allocated)
265 265 return 1;
266 266 if (tmp->state == &malloced)
267 267 return 1;
268 268 } END_FOR_EACH_PTR(tmp);
269 269 return 0;
270 270 }
271 271
272 272 static void check_sm_is_leaked(struct sm_state *sm)
273 273 {
274 274 if (possibly_allocated(sm->possible) &&
275 275 !is_null(sm->name, sm->sym) &&
276 276 !is_argument(sm->sym) &&
277 277 !parent_is_assigned(sm->sym))
278 278 sm_error("memory leak of '%s'", sm->name);
279 279 }
280 280
281 281 static void check_tracker_is_leaked(struct tracker *t)
282 282 {
283 283 struct sm_state *sm;
284 284
285 285 sm = get_sm_state(t->owner, t->name, t->sym);
286 286 if (sm)
287 287 check_sm_is_leaked(sm);
288 288 __free_tracker(t);
289 289 }
290 290
291 291 static void match_declarations(struct symbol *sym)
292 292 {
293 293 const char *name;
294 294
295 295 if ((get_base_type(sym))->type == SYM_ARRAY) {
296 296 return;
297 297 }
298 298
299 299 name = sym->ident->name;
300 300
301 301 if (sym->initializer) {
302 302 if (is_allocation(sym->initializer)) {
303 303 set_state(my_id, name, sym, &malloced);
304 304 add_scope_hook((scope_hook *)&check_tracker_is_leaked,
305 305 alloc_tracker(my_id, name, sym));
306 306 scoped_state(my_id, name, sym);
307 307 } else {
308 308 assign_parent(sym);
309 309 }
310 310 }
311 311 }
312 312
313 313 static void check_for_allocated(void)
314 314 {
315 315 struct stree *stree;
316 316 struct sm_state *tmp;
317 317
318 318 stree = __get_cur_stree();
319 319 FOR_EACH_MY_SM(my_id, stree, tmp) {
320 320 check_sm_is_leaked(tmp);
321 321 } END_FOR_EACH_SM(tmp);
322 322 }
323 323
324 324 static void match_return(struct expression *ret_value)
325 325 {
326 326 char *name;
327 327 struct symbol *sym;
328 328
329 329 if (__inline_fn)
330 330 return;
331 331 name = expr_to_str_sym(ret_value, &sym);
332 332 if (sym)
333 333 assign_parent(sym);
334 334 free_string(name);
335 335 check_for_allocated();
336 336 }
337 337
338 338 static void set_new_true_false_paths(const char *name, struct symbol *sym)
339 339 {
340 340 struct smatch_state *tmp;
341 341
342 342 tmp = get_state(my_id, name, sym);
343 343
344 344 if (!tmp) {
345 345 return;
346 346 }
347 347
348 348 if (tmp == &isfree) {
349 349 sm_warning("why do you care about freed memory? '%s'", name);
350 350 }
351 351
352 352 if (tmp == &assigned) {
353 353 /* we don't care about assigned pointers any more */
354 354 return;
355 355 }
356 356 set_true_false_states(my_id, name, sym, &allocated, &isnull);
357 357 }
358 358
359 359 static void match_condition(struct expression *expr)
360 360 {
361 361 struct symbol *sym;
362 362 char *name;
363 363
364 364 expr = strip_expr(expr);
365 365 switch (expr->type) {
366 366 case EXPR_PREOP:
367 367 case EXPR_SYMBOL:
368 368 case EXPR_DEREF:
369 369 name = expr_to_var_sym(expr, &sym);
370 370 if (!name)
371 371 return;
372 372 set_new_true_false_paths(name, sym);
373 373 free_string(name);
374 374 return;
375 375 case EXPR_ASSIGNMENT:
376 376 /* You have to deal with stuff like if (a = b = c) */
377 377 match_condition(expr->right);
378 378 match_condition(expr->left);
379 379 return;
380 380 default:
381 381 return;
382 382 }
383 383 }
384 384
385 385 static void match_function_call(struct expression *expr)
386 386 {
387 387 struct expression *tmp;
388 388 struct symbol *sym;
389 389 char *name;
390 390 struct sm_state *state;
391 391
392 392 FOR_EACH_PTR(expr->args, tmp) {
393 393 tmp = strip_expr(tmp);
394 394 name = expr_to_str_sym(tmp, &sym);
395 395 if (!name)
396 396 continue;
397 397 if ((state = get_sm_state(my_id, name, sym))) {
398 398 if (possibly_allocated(state->possible)) {
399 399 set_state(my_id, name, sym, &assigned);
400 400 }
401 401 }
402 402 assign_parent(sym);
403 403 free_string(name);
404 404 } END_FOR_EACH_PTR(tmp);
405 405 }
406 406
407 407 static void match_end_func(struct symbol *sym)
408 408 {
409 409 if (__inline_fn)
410 410 return;
411 411 check_for_allocated();
412 412 }
413 413
414 414 static void match_after_func(struct symbol *sym)
415 415 {
416 416 if (__inline_fn)
417 417 return;
418 418 free_trackers_and_list(&arguments);
419 419 }
420 420
421 421 static void register_funcs_from_file(void)
422 422 {
423 423 struct token *token;
424 424 const char *func;
425 425 int arg;
426 426
427 427 token = get_tokens_file("kernel.frees_argument");
428 428 if (!token)
429 429 return;
430 430 if (token_type(token) != TOKEN_STREAMBEGIN)
431 431 return;
432 432 token = token->next;
433 433 while (token_type(token) != TOKEN_STREAMEND) {
434 434 if (token_type(token) != TOKEN_IDENT)
435 435 return;
436 436 func = show_ident(token->ident);
437 437 token = token->next;
438 438 if (token_type(token) != TOKEN_NUMBER)
439 439 return;
440 440 arg = atoi(token->number);
441 441 add_function_hook(func, &match_free_func, INT_PTR(arg));
442 442 token = token->next;
443 443 }
444 444 clear_token_alloc();
445 445 }
446 446
447 447 void check_memory(int id)
448 448 {
449 449 my_id = id;
450 450 add_unmatched_state_hook(my_id, &unmatched_state);
451 451 add_hook(&match_function_def, FUNC_DEF_HOOK);
452 452 add_hook(&match_declarations, DECLARATION_HOOK);
453 453 add_hook(&match_function_call, FUNCTION_CALL_HOOK);
454 454 add_hook(&match_condition, CONDITION_HOOK);
↓ open down ↓ |
454 lines elided |
↑ open up ↑ |
455 455 add_hook(&match_assign, ASSIGNMENT_HOOK);
456 456 add_hook(&match_return, RETURN_HOOK);
457 457 add_hook(&match_end_func, END_FUNC_HOOK);
458 458 add_hook(&match_after_func, AFTER_FUNC_HOOK);
459 459 add_modification_hook(my_id, &set_unfree);
460 460 if (option_project == PROJ_KERNEL) {
461 461 add_function_hook("kfree", &match_free_func, (void *)0);
462 462 register_funcs_from_file();
463 463 } else {
464 464 add_function_hook("free", &match_free_func, (void *)0);
465 + if (option_project == PROJ_ILLUMOS_USER)
466 + add_function_hook("libld_free", &match_free_func,
467 + (void *)0);
465 468 }
466 469 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX