kernel.no_return_funcs: add kunit_do_failed_assertion()
[smatch/bkmgit.git] / check_unchecked_allocation.c
blob89ff946d207754aec29df173a8e0224ba17d6aff
1 /*
2 * Copyright (C) 2010 Dan Carpenter.
3 * Copyright (C) 2021 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
19 #include "smatch.h"
20 #include "smatch_slist.h"
21 #include "smatch_extra.h"
23 static int my_id;
25 #define __GFP_NOFAIL 0x8000u
27 STATE(null);
29 static unsigned long GFP_NOFAIL(void)
31 static unsigned long saved_flags = -1;
32 struct symbol *macro_sym;
34 if (saved_flags != -1)
35 return saved_flags;
37 macro_sym = lookup_macro_symbol("___GFP_NOFAIL");
38 if (!macro_sym)
39 macro_sym = lookup_macro_symbol("__GFP_NOFAIL");
40 if (!macro_sym || !macro_sym->expansion)
41 return __GFP_NOFAIL;
42 if (token_type(macro_sym->expansion) != TOKEN_NUMBER)
43 return __GFP_NOFAIL;
45 saved_flags = strtoul(macro_sym->expansion->number, NULL, 0);
46 return saved_flags;
49 static void is_ok(struct sm_state *sm, struct expression *mod_expr)
51 set_state(my_id, sm->name, sm->sym, &undefined);
54 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
56 struct smatch_state *state;
58 if (is_impossible_path()) {
59 set_state(my_id, cur->name, cur->sym, &undefined);
60 return;
63 state = get_state(SMATCH_EXTRA, cur->name, cur->sym);
64 if (!state || !estate_rl(state))
65 return;
66 if (!rl_has_sval(estate_rl(state), ptr_null))
67 set_state(my_id, cur->name, cur->sym, &undefined);
70 static bool is_possibly_zero(const char *name, struct symbol *sym)
72 struct sm_state *sm, *tmp;
74 sm = get_sm_state(SMATCH_EXTRA, name, sym);
75 if (!sm)
76 return false;
77 FOR_EACH_PTR(sm->possible, tmp) {
78 if (!estate_rl(tmp->state))
79 continue;
80 if (rl_min(estate_rl(tmp->state)).value == 0 &&
81 rl_max(estate_rl(tmp->state)).value == 0)
82 return true;
83 } END_FOR_EACH_PTR(tmp);
85 return false;
88 static const char *get_allocation_fn_name(const char *name, struct symbol *sym)
90 struct expression *expr;
92 expr = get_assigned_expr_name_sym(name, sym);
93 if (!expr)
94 return "<unknown>";
95 if (expr->type == EXPR_CALL &&
96 expr->fn->type == EXPR_SYMBOL &&
97 expr->fn->symbol && expr->fn->symbol->ident)
98 return expr->fn->symbol->ident->name;
100 return "<unknown>";
103 static void check_dereference_name_sym(char *name, struct symbol *sym)
105 struct sm_state *sm;
106 const char *fn_name;
108 sm = get_sm_state(my_id, name, sym);
109 if (!sm)
110 return;
111 if (is_ignored(my_id, sm->name, sm->sym))
112 return;
113 if (!is_possibly_zero(sm->name, sm->sym))
114 return;
115 if (is_impossible_path())
116 return;
117 if (!slist_has_state(sm->possible, &null))
118 return;
120 sm_msg("%s: sm='%s'", __func__, show_sm(sm));
121 fn_name = get_allocation_fn_name(name, sym);
122 sm_error("potential null dereference '%s'. (%s returns null)",
123 sm->name, fn_name);
126 static void check_dereference(struct expression *expr)
128 char *name;
129 struct symbol *sym;
131 name = expr_to_var_sym(expr, &sym);
132 if (!name)
133 return;
134 check_dereference_name_sym(name, sym);
135 free_string(name);
138 static void match_dereferences(struct expression *expr)
140 if (expr->type != EXPR_PREOP)
141 return;
142 check_dereference(expr->unop);
145 static void match_pointer_as_array(struct expression *expr)
147 if (!is_array(expr))
148 return;
149 check_dereference(get_array_base(expr));
152 static void set_param_dereferenced(struct expression *call, struct expression *arg, char *key, char *unused)
154 struct symbol *sym;
155 char *name;
157 name = get_variable_from_key(arg, key, &sym);
158 if (!name || !sym)
159 goto free;
161 check_dereference_name_sym(name, sym);
162 free:
163 free_string(name);
166 static int called_with_no_fail(struct expression *call, int param)
168 struct expression *arg;
169 sval_t sval;
171 if (param == -1)
172 return 0;
173 call = strip_expr(call);
174 if (call->type != EXPR_CALL)
175 return 0;
176 arg = get_argument_from_call_expr(call->args, param);
177 if (get_value(arg, &sval) && (sval.uvalue & GFP_NOFAIL()))
178 return 1;
179 return 0;
182 static void match_assign_returns_null(const char *fn, struct expression *expr, void *_gfp)
184 int gfp_param = PTR_INT(_gfp);
186 if (called_with_no_fail(expr->right, gfp_param))
187 return;
188 set_state_expr(my_id, expr->left, &null);
191 static void register_allocation_funcs(void)
193 char filename[256];
194 struct token *token;
195 const char *func;
196 int arg;
198 snprintf(filename, sizeof(filename), "%s.allocation_funcs_gfp", option_project_str);
199 token = get_tokens_file(filename);
200 if (!token)
201 return;
202 if (token_type(token) != TOKEN_STREAMBEGIN)
203 return;
204 token = token->next;
205 while (token_type(token) != TOKEN_STREAMEND) {
206 if (token_type(token) != TOKEN_IDENT) {
207 printf("error parsing '%s' line %d\n", filename, token->pos.line);
208 return;
210 func = show_ident(token->ident);
211 token = token->next;
212 if (token_type(token) == TOKEN_IDENT)
213 arg = -1;
214 else if (token_type(token) == TOKEN_NUMBER)
215 arg = atoi(token->number);
216 else {
217 printf("error parsing '%s' line %d\n", filename, token->pos.line);
218 return;
220 add_function_assign_hook(func, &match_assign_returns_null, INT_PTR(arg));
221 token = token->next;
223 clear_token_alloc();
226 void check_unchecked_allocation(int id)
228 my_id = id;
230 add_modification_hook(my_id, &is_ok);
231 add_pre_merge_hook(my_id, &pre_merge_hook);
232 add_hook(&match_dereferences, DEREF_HOOK);
233 add_hook(&match_pointer_as_array, OP_HOOK);
234 select_return_implies_hook(DEREFERENCE, &set_param_dereferenced);
235 register_allocation_funcs();