unchecked_allocation: warn about potential NULL dereferences
[smatch.git] / check_free_strict.c
blob38eeefe4bebbd4e227f9fabab94f51524c493be4
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, "$" },
74 { "qdisc_enqueue", PARAM_FREED, 0, "$" },
76 { "kobject_put", PARAM_FREED, 0, "$", NULL, NULL, &match_kobject_put },
77 { "kref_put", PARAM_FREED, 0, "$", NULL, NULL, &match_kobject_put },
78 { "put_device", PARAM_FREED, 0, "$", NULL, NULL, &match_kobject_put },
81 static struct name_sym_fn_list *free_hooks;
83 void add_free_hook(name_sym_hook *hook)
85 add_ptr_list(&free_hooks, hook);
88 static void call_free_call_backs_name_sym(struct expression *expr, const char *name, struct symbol *sym)
90 name_sym_hook *hook;
92 if (!expr)
93 expr = gen_expression_from_name_sym(name, sym);
95 FOR_EACH_PTR(free_hooks, hook) {
96 hook(expr, name, sym);
97 } END_FOR_EACH_PTR(hook);
100 static void ok_to_use(struct sm_state *sm, struct expression *mod_expr)
102 if (sm->state != &ok)
103 set_state(my_id, sm->name, sm->sym, &ok);
106 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
108 if (is_impossible_path())
109 set_state(my_id, cur->name, cur->sym, &ok);
112 static struct smatch_state *unmatched_state(struct sm_state *sm)
114 struct smatch_state *state;
115 sval_t sval;
117 if (sm->state != &freed && sm->state != &maybe_freed)
118 return &undefined;
120 if (get_param_num_from_sym(sm->sym) < 0)
121 return &undefined;
124 * If the parent is non-there count it as freed. This is
125 * a hack for tracking return states.
127 if (parent_is_null_var_sym(sm->name, sm->sym))
128 return sm->state;
130 state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
131 if (!state)
132 return &undefined;
133 if (!estate_get_single_value(state, &sval) || sval.value != 0)
134 return &undefined;
135 /* It makes it easier to consider NULL pointers as freed. */
136 return &freed;
139 struct smatch_state *merge_frees(struct smatch_state *s1, struct smatch_state *s2)
141 if (s1 == &freed && s2 == &maybe_freed)
142 return &maybe_freed;
143 if (s1 == &maybe_freed && s2 == &freed)
144 return &maybe_freed;
145 return &merged;
148 static int is_freed(struct expression *expr)
150 struct sm_state *sm;
152 sm = get_sm_state_expr(my_id, expr);
153 if (sm && slist_has_state(sm->possible, &freed))
154 return 1;
155 return 0;
158 bool is_freed_var_sym(const char *name, struct symbol *sym)
160 struct smatch_state *state;
162 state = get_state(my_id, name, sym);
163 if (state == &freed || state == &maybe_freed)
164 return true;
166 return false;
169 static bool expr_is_condition(struct expression *expr)
171 struct statement *stmt;
173 stmt = expr_get_parent_stmt(expr);
174 if (!stmt)
175 return false;
176 if (stmt->type == STMT_IF || stmt->type == STMT_ITERATOR)
177 return true;
178 return false;
181 bool is_part_of_condition(struct expression *expr)
183 struct expression *parent;
185 if (expr_is_condition(expr))
186 return true;
188 parent = expr_get_parent_expr(expr);
189 if (!parent)
190 return false;
191 if (parent->type == EXPR_LOGICAL || parent->type == EXPR_COMPARE)
192 return true;
193 if (parent->type == EXPR_SELECT || parent->type == EXPR_CONDITIONAL)
194 return true;
195 if (parent->type == EXPR_PREOP && parent->op == '!')
196 return true;
198 return false;
201 static bool is_percent_p(struct expression *str_expr, int idx)
203 char *p;
204 int cnt = 0;
206 p = str_expr->string->data;
207 while (p[0]) {
208 if (p[0] == '%' && p[1] == '%') {
209 p += 2;
210 continue;
212 /* If we have print("%.*s %p", prec, str, p); then it takes 2 params */
213 if ((p[0] == '%' && p[1] == '*') ||
214 (p[0] == '%' && p[1] == '.' && p[2] == '*'))
215 cnt++;
216 if (p[0] == '%') {
217 cnt++;
218 if (idx == cnt && p[1] == 'p')
219 return true;
221 p++;
223 return false;
226 bool is_percent_p_print(struct expression *expr)
228 struct expression *parent, *arg;
229 int expr_idx, string_idx;
231 parent = expr_get_parent_expr(expr);
232 if (!parent || parent->type != EXPR_CALL)
233 return false;
235 expr_idx = -1;
236 FOR_EACH_PTR(parent->args, arg) {
237 expr_idx++;
238 if (arg == expr)
239 goto found;
240 } END_FOR_EACH_PTR(arg);
242 return false;
243 found:
245 string_idx = -1;
246 FOR_EACH_PTR(parent->args, arg) {
247 string_idx++;
248 if (arg->type != EXPR_STRING)
249 continue;
250 if (is_percent_p(arg, expr_idx - string_idx))
251 return true;
252 } END_FOR_EACH_PTR(arg);
254 return false;
257 static void match_symbol(struct expression *expr)
259 struct expression *parent;
260 char *name;
262 if (is_impossible_path())
263 return;
264 if (__in_fake_parameter_assign)
265 return;
267 if (is_part_of_condition(expr))
268 return;
270 /* This ignores stuff like "get_new_ptr(&foo);" */
271 parent = expr_get_parent_expr(expr);
272 while (parent && parent->type == EXPR_PREOP && parent->op == '(')
273 parent = expr_get_parent_expr(parent);
274 if (parent && parent->type == EXPR_PREOP && parent->op == '&')
275 return;
277 if (!is_freed(expr))
278 return;
280 if (is_percent_p_print(expr))
281 return;
283 name = expr_to_var(expr);
284 sm_warning("'%s' was already freed.", name);
285 free_string(name);
288 static void match_dereferences(struct expression *expr)
290 char *name;
292 if (expr->type != EXPR_PREOP)
293 return;
295 if (is_impossible_path())
296 return;
297 if (__in_fake_parameter_assign)
298 return;
300 expr = strip_expr(expr->unop);
301 if (!is_freed(expr))
302 return;
303 name = expr_to_var(expr);
304 sm_error("dereferencing freed memory '%s'", name);
305 set_state_expr(my_id, expr, &ok);
306 free_string(name);
309 static int ignored_params[16];
311 static void set_ignored_params(struct expression *call)
313 struct expression *arg;
314 const char *p;
315 int i;
317 memset(&ignored_params, 0, sizeof(ignored_params));
319 i = -1;
320 FOR_EACH_PTR(call->args, arg) {
321 i++;
322 if (arg->type != EXPR_STRING)
323 continue;
324 goto found;
325 } END_FOR_EACH_PTR(arg);
327 return;
329 found:
330 i++;
331 p = arg->string->data;
332 while ((p = strchr(p, '%'))) {
333 if (i >= ARRAY_SIZE(ignored_params))
334 return;
335 p++;
336 if (*p == '%') {
337 p++;
338 continue;
340 if (*p == '.')
341 p++;
342 if (*p == '*')
343 i++;
344 if (*p == 'p')
345 ignored_params[i] = 1;
346 i++;
350 static int is_free_func(struct expression *fn)
352 char *name;
353 int ret = 0;
355 name = expr_to_str(fn);
356 if (!name)
357 return 0;
358 if (strstr(name, "free"))
359 ret = 1;
360 free_string(name);
362 return ret;
365 static void match_call(struct expression *expr)
367 struct expression *arg;
368 char *name;
369 int i;
371 if (is_impossible_path())
372 return;
374 set_ignored_params(expr);
376 i = -1;
377 FOR_EACH_PTR(expr->args, arg) {
378 i++;
379 if (!is_pointer(arg))
380 continue;
381 if (!is_freed(arg))
382 continue;
383 if (ignored_params[i])
384 continue;
385 if (is_percent_p_print(arg))
386 continue;
388 name = expr_to_var(arg);
389 if (is_free_func(expr->fn))
390 sm_error("double free of '%s'", name);
391 else
392 sm_warning("passing freed memory '%s'", name);
393 set_state_expr(my_id, arg, &ok);
394 free_string(name);
395 } END_FOR_EACH_PTR(arg);
398 static void match_return(struct expression *expr)
400 char *name;
402 if (is_impossible_path())
403 return;
405 if (!expr)
406 return;
407 if (!is_freed(expr))
408 return;
410 name = expr_to_var(expr);
411 sm_warning("returning freed memory '%s'", name);
412 set_state_expr(my_id, expr, &ok);
413 free_string(name);
416 static int counter_was_inced_name_sym(const char *name, struct symbol *sym, const char *counter_str)
418 char buf[256];
420 snprintf(buf, sizeof(buf), "%s%s", name, counter_str);
421 return was_inced(buf, sym);
424 static int counter_was_inced(struct expression *expr, const char *counter_str)
426 char *name;
427 struct symbol *sym;
428 int ret = 0;
430 name = expr_to_var_sym(expr, &sym);
431 if (!name || !sym)
432 goto free;
434 ret = counter_was_inced_name_sym(name, sym, counter_str);
435 free:
436 free_string(name);
437 return ret;
440 static bool is_ptr_to(struct expression *expr, const char *type)
442 struct symbol *sym;
444 sym = get_type(expr);
445 if (!is_ptr_type(sym))
446 return false;
447 sym = get_real_base_type(sym);
448 if (sym && sym->ident && strcmp(sym->ident->name, type) == 0)
449 return true;
450 return false;
453 static void match_free(struct expression *expr, const char *name, struct symbol *sym, void *data)
455 struct expression *arg;
457 if (is_impossible_path())
458 return;
460 arg = gen_expression_from_name_sym(name, sym);
461 if (!arg)
462 return;
463 if (is_ptr_to(arg, "sk_buff") &&
464 counter_was_inced(arg, "->users.refs.counter"))
465 return;
466 if (is_ptr_to(arg, "buffer_head") &&
467 counter_was_inced(arg, "->b_count.counter"))
468 return;
469 if (is_freed(arg))
470 sm_error("double free of '%s'", name);
472 track_freed_param(arg, &freed);
473 call_free_call_backs_name_sym(expr, name, sym);
474 set_state_expr(my_id, arg, &freed);
478 static void match_kobject_put(struct expression *expr, const char *name, struct symbol *sym, void *data)
480 struct expression *arg;
482 arg = gen_expression_from_name_sym(name, sym);
483 if (!arg)
484 return;
485 /* kobject_put(&cdev->kobj); */
486 if (arg->type != EXPR_PREOP || arg->op != '&')
487 return;
488 arg = strip_expr(arg->unop);
489 if (arg->type != EXPR_DEREF)
490 return;
491 arg = strip_expr(arg->deref);
492 if (arg->type != EXPR_PREOP || arg->op != '*')
493 return;
494 arg = strip_expr(arg->unop);
495 track_freed_param(arg, &maybe_freed);
496 set_state_expr(my_id, arg, &maybe_freed);
499 struct string_list *handled;
500 static bool is_handled_func(struct expression *fn)
502 if (!fn || fn->type != EXPR_SYMBOL || !fn->symbol->ident)
503 return false;
505 return list_has_string(handled, fn->symbol->ident->name);
508 static void set_param_helper(struct expression *expr, int param,
509 char *key, char *value,
510 struct smatch_state *state)
512 struct expression *arg;
513 char *name;
514 struct symbol *sym;
515 struct sm_state *sm;
517 while (expr->type == EXPR_ASSIGNMENT)
518 expr = strip_expr(expr->right);
519 if (expr->type != EXPR_CALL)
520 return;
522 if (is_handled_func(expr->fn))
523 return;
525 arg = get_argument_from_call_expr(expr->args, param);
526 if (!arg)
527 return;
528 name = get_variable_from_key(arg, key, &sym);
529 if (!name || !sym)
530 goto free;
532 /* skbs are not free if we called skb_get(). */
533 if (counter_was_inced_name_sym(name, sym, "->users.refs.counter"))
534 goto free;
536 if (state == &freed && !is_impossible_path()) {
537 sm = get_sm_state(my_id, name, sym);
538 if (sm && slist_has_state(sm->possible, &freed)) {
539 sm_warning("'%s' double freed", name);
540 set_state(my_id, name, sym, &ok); /* fixme: doesn't silence anything. I know */
544 track_freed_param_var_sym(name, sym, state);
545 if (state == &freed)
546 call_free_call_backs_name_sym(NULL, name, sym);
547 set_state(my_id, name, sym, state);
548 free:
549 free_string(name);
552 static void set_param_freed(struct expression *expr, int param, char *key, char *value)
554 set_param_helper(expr, param, key, value, &freed);
557 static void set_param_maybe_freed(struct expression *expr, int param, char *key, char *value)
559 set_param_helper(expr, param, key, value, &maybe_freed);
562 int parent_is_free_var_sym_strict(const char *name, struct symbol *sym)
564 char buf[256];
565 char *start;
566 char *end;
567 char orig;
568 struct smatch_state *state;
570 strncpy(buf, name, sizeof(buf) - 1);
571 buf[sizeof(buf) - 1] = '\0';
573 start = &buf[0];
574 while ((*start == '&'))
575 start++;
577 end = start;
578 while ((end = strrchr(end, '-'))) {
579 orig = *end;
580 *end = '\0';
582 state = __get_state(my_id, start, sym);
583 if (state == &freed)
584 return 1;
585 *end = orig;
586 end++;
588 return 0;
591 int parent_is_free_strict(struct expression *expr)
593 struct symbol *sym;
594 char *var;
595 int ret = 0;
597 expr = strip_expr(expr);
598 var = expr_to_var_sym(expr, &sym);
599 if (!var || !sym)
600 goto free;
601 ret = parent_is_free_var_sym_strict(var, sym);
602 free:
603 free_string(var);
604 return ret;
607 static void match_untracked(struct expression *call, int param)
609 struct state_list *slist = NULL;
610 struct expression *arg;
611 struct sm_state *sm;
612 char *name;
613 char buf[64];
614 int len;
616 arg = get_argument_from_call_expr(call->args, param);
617 if (!arg)
618 return;
620 name = expr_to_var(arg);
621 if (!name)
622 return;
623 snprintf(buf, sizeof(buf), "%s->", name);
624 free_string(name);
625 len = strlen(buf);
627 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
628 if (strncmp(sm->name, buf, len) == 0)
629 add_ptr_list(&slist, sm);
630 } END_FOR_EACH_SM(sm);
632 FOR_EACH_PTR(slist, sm) {
633 set_state(sm->owner, sm->name, sm->sym, &ok);
634 } END_FOR_EACH_PTR(sm);
636 free_slist(&slist);
639 void check_free_strict(int id)
641 struct func_info *info;
642 param_key_hook *cb;
643 int i;
645 my_id = id;
647 if (option_project != PROJ_KERNEL)
648 return;
650 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
651 info = &func_table[i];
653 insert_string(&handled, info->name);
655 if (info->call_back)
656 cb = info->call_back;
657 else
658 cb = &match_free;
660 if (info->implies_start) {
661 return_implies_param_key(info->name,
662 *info->implies_start, *info->implies_end,
663 cb, info->param, info->key, info);
664 } else {
665 add_function_param_key_hook(info->name, cb,
666 info->param, info->key, info);
670 if (option_spammy)
671 add_hook(&match_symbol, SYM_HOOK);
672 add_hook(&match_dereferences, DEREF_HOOK);
673 add_hook(&match_call, FUNCTION_CALL_HOOK);
674 add_hook(&match_return, RETURN_HOOK);
676 add_modification_hook_late(my_id, &ok_to_use);
677 add_pre_merge_hook(my_id, &pre_merge_hook);
678 add_unmatched_state_hook(my_id, &unmatched_state);
679 add_merge_hook(my_id, &merge_frees);
681 select_return_states_hook(PARAM_FREED, &set_param_freed);
682 select_return_states_hook(MAYBE_FREED, &set_param_maybe_freed);
683 add_untracked_param_hook(&match_untracked);