units: move checks to check_ file and hide under the --spammy option
[smatch.git] / check_free_strict.c
blobd397103aaa8ccddac6985e8fc9144921fa1695c8
1 /*
2 * Copyright (C) 2010 Dan Carpenter.
3 * Copyright (C) 2020 Oracle.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
20 * This is the "strict" version which is more daring and ambitious than
21 * the check_free.c file. The difference is that this looks at split
22 * returns and the other only looks at if every path frees a parameter.
23 * Also this has a bunch of kernel specific things to do with reference
24 * counted memory.
27 #include <string.h>
28 #include "smatch.h"
29 #include "smatch_slist.h"
30 #include "smatch_extra.h"
32 static int my_id;
34 STATE(freed);
35 STATE(maybe_freed);
36 STATE(ok);
38 static void match_kobject_put(struct expression *expr, const char *name, struct symbol *sym, void *data);
40 struct func_info {
41 const char *name;
42 int type;
43 int param;
44 const char *key;
45 const sval_t *implies_start, *implies_end;
46 param_key_hook *call_back;
49 static struct func_info func_table[] = {
50 { "free", PARAM_FREED, 0, "$" },
51 { "kfree", PARAM_FREED, 0, "$" },
52 { "vfree", PARAM_FREED, 0, "$" },
53 { "kzfree", PARAM_FREED, 0, "$" },
54 { "kvfree", PARAM_FREED, 0, "$" },
56 { "kfree_skb", PARAM_FREED, 0, "$" },
57 { "kfree_skbmem", PARAM_FREED, 0, "$" },
59 { "mempool_free", PARAM_FREED, 0, "$" },
60 { "kmem_cache_free", PARAM_FREED, 1, "$" },
61 { "dma_pool_free", PARAM_FREED, 1, "$" },
63 { "memstick_free_host", PARAM_FREED, 0, "$" },
64 // { "spi_unregister_controller", PARAM_FREED, 0, "$" },
65 { "netif_rx_internal", PARAM_FREED, 0, "$" },
66 { "netif_rx", PARAM_FREED, 0, "$" },
68 { "enqueue_to_backlog", PARAM_FREED, 0, "$" },
70 { "brelse", PARAM_FREED, 0, "$" },
71 { "dma_free_coherent", PARAM_FREED, 2, "$" },
72 { "free_netdev", PARAM_FREED, 0, "$" },
73 { "sock_release", PARAM_FREED, 0, "$" },
75 { "qdisc_enqueue", PARAM_FREED, 0, "$" },
77 { "kobject_put", PARAM_FREED, 0, "$", NULL, NULL, &match_kobject_put },
78 { "kref_put", PARAM_FREED, 0, "$", NULL, NULL, &match_kobject_put },
79 { "put_device", PARAM_FREED, 0, "$", NULL, NULL, &match_kobject_put },
82 static struct name_sym_fn_list *free_hooks;
84 void add_free_hook(name_sym_hook *hook)
86 add_ptr_list(&free_hooks, hook);
89 static void call_free_call_backs_name_sym(struct expression *expr, const char *name, struct symbol *sym)
91 name_sym_hook *hook;
93 if (!expr)
94 expr = gen_expression_from_name_sym(name, sym);
96 FOR_EACH_PTR(free_hooks, hook) {
97 hook(expr, name, sym);
98 } END_FOR_EACH_PTR(hook);
101 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
103 if (sm->state != &ok)
104 set_state(my_id, sm->name, sm->sym, &ok);
107 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
109 if (is_impossible_path())
110 set_state(my_id, cur->name, cur->sym, &ok);
113 static struct smatch_state *unmatched_state(struct sm_state *sm)
115 struct smatch_state *state;
116 sval_t sval;
118 if (sm->state != &freed && sm->state != &maybe_freed)
119 return &undefined;
121 if (get_param_num_from_sym(sm->sym) < 0)
122 return &undefined;
125 * If the parent is non-there count it as freed. This is
126 * a hack for tracking return states.
128 if (parent_is_null_var_sym(sm->name, sm->sym))
129 return sm->state;
131 state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
132 if (!state)
133 return &undefined;
134 if (!estate_get_single_value(state, &sval) || sval.value != 0)
135 return &undefined;
136 /* It makes it easier to consider NULL pointers as freed. */
137 return sm->state;
140 struct smatch_state *merge_frees(struct smatch_state *s1, struct smatch_state *s2)
142 if (s1 == &freed && s2 == &maybe_freed)
143 return &maybe_freed;
144 if (s1 == &maybe_freed && s2 == &freed)
145 return &maybe_freed;
146 return &merged;
149 static int is_freed(struct expression *expr)
151 struct sm_state *sm;
153 sm = get_sm_state_expr(my_id, expr);
154 if (sm && slist_has_state(sm->possible, &freed))
155 return 1;
156 return 0;
159 bool is_freed_var_sym(const char *name, struct symbol *sym)
161 struct smatch_state *state;
163 state = get_state(my_id, name, sym);
164 if (state == &freed || state == &maybe_freed)
165 return true;
167 return false;
170 static bool expr_is_condition(struct expression *expr)
172 struct statement *stmt;
174 stmt = expr_get_parent_stmt(expr);
175 if (!stmt)
176 return false;
177 if (stmt->type == STMT_IF || stmt->type == STMT_ITERATOR)
178 return true;
179 return false;
182 bool is_part_of_condition(struct expression *expr)
184 struct expression *parent;
186 if (expr_is_condition(expr))
187 return true;
189 parent = expr_get_parent_expr(expr);
190 if (!parent)
191 return false;
192 if (parent->type == EXPR_LOGICAL || parent->type == EXPR_COMPARE)
193 return true;
194 if (parent->type == EXPR_SELECT || parent->type == EXPR_CONDITIONAL)
195 return true;
196 if (parent->type == EXPR_PREOP && parent->op == '!')
197 return true;
199 return false;
202 static bool is_percent_p(struct expression *str_expr, int idx)
204 char *p;
205 int cnt = 0;
207 p = str_expr->string->data;
208 while (p[0]) {
209 if (p[0] == '%' && p[1] == '%') {
210 p += 2;
211 continue;
213 /* If we have print("%.*s %p", prec, str, p); then it takes 2 params */
214 if ((p[0] == '%' && p[1] == '*') ||
215 (p[0] == '%' && p[1] == '.' && p[2] == '*'))
216 cnt++;
217 if (p[0] == '%') {
218 cnt++;
219 if (idx == cnt && p[1] == 'p')
220 return true;
222 p++;
224 return false;
227 bool is_percent_p_print(struct expression *expr)
229 struct expression *parent, *arg;
230 int expr_idx, string_idx;
232 parent = expr_get_parent_expr(expr);
233 if (!parent || parent->type != EXPR_CALL)
234 return false;
236 expr_idx = -1;
237 FOR_EACH_PTR(parent->args, arg) {
238 expr_idx++;
239 if (arg == expr)
240 goto found;
241 } END_FOR_EACH_PTR(arg);
243 return false;
244 found:
246 string_idx = -1;
247 FOR_EACH_PTR(parent->args, arg) {
248 string_idx++;
249 if (arg->type != EXPR_STRING)
250 continue;
251 if (is_percent_p(arg, expr_idx - string_idx))
252 return true;
253 } END_FOR_EACH_PTR(arg);
255 return false;
258 static void match_symbol(struct expression *expr)
260 struct expression *parent;
261 char *name;
263 if (is_impossible_path())
264 return;
265 if (__in_fake_parameter_assign)
266 return;
268 if (is_part_of_condition(expr))
269 return;
271 /* This ignores stuff like "get_new_ptr(&foo);" */
272 parent = expr_get_parent_expr(expr);
273 while (parent && parent->type == EXPR_PREOP && parent->op == '(')
274 parent = expr_get_parent_expr(parent);
275 if (parent && parent->type == EXPR_PREOP && parent->op == '&')
276 return;
278 if (!is_freed(expr))
279 return;
281 if (is_percent_p_print(expr))
282 return;
284 name = expr_to_var(expr);
285 sm_warning("'%s' was already freed.", name);
286 free_string(name);
289 static void match_dereferences(struct expression *expr)
291 char *name;
293 if (expr->type != EXPR_PREOP)
294 return;
296 if (is_impossible_path())
297 return;
298 if (__in_fake_parameter_assign)
299 return;
301 expr = strip_expr(expr->unop);
302 if (!is_freed(expr))
303 return;
304 name = expr_to_var(expr);
305 sm_error("dereferencing freed memory '%s'", name);
306 set_state_expr(my_id, expr, &ok);
307 free_string(name);
310 static int ignored_params[16];
312 static void set_ignored_params(struct expression *call)
314 struct expression *arg;
315 const char *p;
316 int i;
318 memset(&ignored_params, 0, sizeof(ignored_params));
320 i = -1;
321 FOR_EACH_PTR(call->args, arg) {
322 i++;
323 if (arg->type != EXPR_STRING)
324 continue;
325 goto found;
326 } END_FOR_EACH_PTR(arg);
328 return;
330 found:
331 i++;
332 p = arg->string->data;
333 while ((p = strchr(p, '%'))) {
334 if (i >= ARRAY_SIZE(ignored_params))
335 return;
336 p++;
337 if (*p == '%') {
338 p++;
339 continue;
341 if (*p == '.')
342 p++;
343 if (*p == '*')
344 i++;
345 if (*p == 'p')
346 ignored_params[i] = 1;
347 i++;
351 static int is_free_func(struct expression *fn)
353 char *name;
354 int ret = 0;
356 name = expr_to_str(fn);
357 if (!name)
358 return 0;
359 if (strstr(name, "free"))
360 ret = 1;
361 free_string(name);
363 return ret;
366 static void match_call(struct expression *expr)
368 struct expression *arg;
369 char *name;
370 int i;
372 if (is_impossible_path())
373 return;
375 set_ignored_params(expr);
377 i = -1;
378 FOR_EACH_PTR(expr->args, arg) {
379 i++;
380 if (!is_pointer(arg))
381 continue;
382 if (!is_freed(arg))
383 continue;
384 if (ignored_params[i])
385 continue;
386 if (is_percent_p_print(arg))
387 continue;
389 name = expr_to_var(arg);
390 if (is_free_func(expr->fn))
391 sm_error("double free of '%s'", name);
392 else
393 sm_warning("passing freed memory '%s'", name);
394 set_state_expr(my_id, arg, &ok);
395 free_string(name);
396 } END_FOR_EACH_PTR(arg);
399 static void match_return(struct expression *expr)
401 char *name;
403 if (is_impossible_path())
404 return;
406 if (!expr)
407 return;
408 if (!is_freed(expr))
409 return;
411 name = expr_to_var(expr);
412 sm_warning("returning freed memory '%s'", name);
413 set_state_expr(my_id, expr, &ok);
414 free_string(name);
417 static int counter_was_inced_name_sym(const char *name, struct symbol *sym, const char *counter_str)
419 char buf[256];
421 snprintf(buf, sizeof(buf), "%s%s", name, counter_str);
422 return was_inced(buf, sym);
425 static int counter_was_inced(struct expression *expr, const char *counter_str)
427 char *name;
428 struct symbol *sym;
429 int ret = 0;
431 name = expr_to_var_sym(expr, &sym);
432 if (!name || !sym)
433 goto free;
435 ret = counter_was_inced_name_sym(name, sym, counter_str);
436 free:
437 free_string(name);
438 return ret;
441 static bool is_ptr_to(struct expression *expr, const char *type)
443 struct symbol *sym;
445 sym = get_type(expr);
446 if (!is_ptr_type(sym))
447 return false;
448 sym = get_real_base_type(sym);
449 if (sym && sym->ident && strcmp(sym->ident->name, type) == 0)
450 return true;
451 return false;
454 static void match_free(struct expression *expr, const char *name, struct symbol *sym, void *data)
456 struct expression *arg;
458 if (is_impossible_path())
459 return;
461 arg = gen_expression_from_name_sym(name, sym);
462 if (!arg)
463 return;
464 if (is_ptr_to(arg, "sk_buff") &&
465 counter_was_inced(arg, "->users.refs.counter"))
466 return;
467 if (is_ptr_to(arg, "buffer_head") &&
468 counter_was_inced(arg, "->b_count.counter"))
469 return;
470 if (is_freed(arg))
471 sm_error("double free of '%s'", name);
473 track_freed_param(arg, &freed);
474 call_free_call_backs_name_sym(expr, name, sym);
475 set_state_expr(my_id, arg, &freed);
479 static void match_kobject_put(struct expression *expr, const char *name, struct symbol *sym, void *data)
481 struct expression *arg;
483 arg = gen_expression_from_name_sym(name, sym);
484 if (!arg)
485 return;
486 /* kobject_put(&cdev->kobj); */
487 if (arg->type != EXPR_PREOP || arg->op != '&')
488 return;
489 arg = strip_expr(arg->unop);
490 if (arg->type != EXPR_DEREF)
491 return;
492 arg = strip_expr(arg->deref);
493 if (arg->type != EXPR_PREOP || arg->op != '*')
494 return;
495 arg = strip_expr(arg->unop);
496 track_freed_param(arg, &maybe_freed);
497 set_state_expr(my_id, arg, &maybe_freed);
500 struct string_list *handled;
501 static bool is_handled_func(struct expression *fn)
503 if (!fn || fn->type != EXPR_SYMBOL || !fn->symbol->ident)
504 return false;
506 return list_has_string(handled, fn->symbol->ident->name);
509 static void set_param_helper(struct expression *expr, int param,
510 char *key, char *value,
511 struct smatch_state *state)
513 struct expression *arg;
514 char *name;
515 struct symbol *sym;
516 struct sm_state *sm;
518 while (expr->type == EXPR_ASSIGNMENT)
519 expr = strip_expr(expr->right);
520 if (expr->type != EXPR_CALL)
521 return;
523 if (is_handled_func(expr->fn))
524 return;
526 arg = get_argument_from_call_expr(expr->args, param);
527 if (!arg)
528 return;
529 name = get_variable_from_key(arg, key, &sym);
530 if (!name || !sym)
531 goto free;
533 /* skbs are not free if we called skb_get(). */
534 if (counter_was_inced_name_sym(name, sym, "->users.refs.counter"))
535 goto free;
537 if (state == &freed && !is_impossible_path()) {
538 sm = get_sm_state(my_id, name, sym);
539 if (sm && slist_has_state(sm->possible, &freed)) {
540 sm_warning("'%s' double freed", name);
541 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */
545 track_freed_param_var_sym(name, sym, state);
546 if (state == &freed)
547 call_free_call_backs_name_sym(NULL, name, sym);
548 set_state(my_id, name, sym, state);
549 free:
550 free_string(name);
553 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
555 set_param_helper(expr, param, key, value, &freed);
558 static void set_param_maybe_freed(struct expression *expr, int param, char *key, char *value)
560 set_param_helper(expr, param, key, value, &maybe_freed);
563 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
565 char buf[256];
566 char *start;
567 char *end;
568 char orig;
569 struct smatch_state *state;
571 strncpy(buf, name, sizeof(buf) - 1);
572 buf[sizeof(buf) - 1] = '\0';
574 start = &buf[0];
575 while ((*start == '&'))
576 start++;
578 end = start;
579 while ((end = strrchr(end, '-'))) {
580 orig = *end;
581 *end = '\0';
583 state = __get_state(my_id, start, sym);
584 if (state == &freed)
585 return 1;
586 *end = orig;
587 end++;
589 return 0;
592 int parent_is_free_strict(struct expression *expr)
594 struct symbol *sym;
595 char *var;
596 int ret = 0;
598 expr = strip_expr(expr);
599 var = expr_to_var_sym(expr, &sym);
600 if (!var || !sym)
601 goto free;
602 ret = parent_is_free_var_sym_strict(var, sym);
603 free:
604 free_string(var);
605 return ret;
608 static void match_untracked(struct expression *call, int param)
610 struct state_list *slist = NULL;
611 struct expression *arg;
612 struct sm_state *sm;
613 char *name;
614 char buf[64];
615 int len;
617 arg = get_argument_from_call_expr(call->args, param);
618 if (!arg)
619 return;
621 name = expr_to_var(arg);
622 if (!name)
623 return;
624 snprintf(buf, sizeof(buf), "%s->", name);
625 free_string(name);
626 len = strlen(buf);
628 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
629 if (strncmp(sm->name, buf, len) == 0)
630 add_ptr_list(&slist, sm);
631 } END_FOR_EACH_SM(sm);
633 FOR_EACH_PTR(slist, sm) {
634 set_state(sm->owner, sm->name, sm->sym, &ok);
635 } END_FOR_EACH_PTR(sm);
637 free_slist(&slist);
640 void check_free_strict(int id)
642 struct func_info *info;
643 param_key_hook *cb;
644 int i;
646 my_id = id;
648 if (option_project != PROJ_KERNEL)
649 return;
651 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
652 info = &func_table[i];
654 insert_string(&handled, info->name);
656 if (info->call_back)
657 cb = info->call_back;
658 else
659 cb = &match_free;
661 if (info->implies_start) {
662 return_implies_param_key(info->name,
663 *info->implies_start, *info->implies_end,
664 cb, info->param, info->key, info);
665 } else {
666 add_function_param_key_hook(info->name, cb,
667 info->param, info->key, info);
671 if (option_spammy)
672 add_hook(&match_symbol, SYM_HOOK);
673 add_hook(&match_dereferences, DEREF_HOOK);
674 add_hook(&match_call, FUNCTION_CALL_HOOK);
675 add_hook(&match_return, RETURN_HOOK);
677 add_modification_hook_late(my_id, &ok_to_use);
678 add_pre_merge_hook(my_id, &pre_merge_hook);
679 add_unmatched_state_hook(my_id, &unmatched_state);
680 add_merge_hook(my_id, &merge_frees);
682 select_return_states_hook(PARAM_FREED, &set_param_freed);
683 select_return_states_hook(MAYBE_FREED, &set_param_maybe_freed);
684 add_untracked_param_hook(&match_untracked);