db/fixup_kernel.sh: fix kstrtoint() functions
[smatch.git] / check_unwind.c
blob3105e309ac5030ad15db8780929bd455c318a54f
1 /*
2 * Copyright (C) 2020 Oracle.
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.
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.
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
18 #include <ctype.h>
20 #include "smatch.h"
21 #include "smatch_extra.h"
22 #include "smatch_slist.h"
24 static int my_id;
25 static int info_id;
27 #define IGNORE 100000
29 STATE(alloc);
30 STATE(release);
31 STATE(param_released);
32 STATE(ignore);
33 STATE(unknown);
35 static unsigned long fn_has_alloc;
37 struct ref_func_info {
38 const char *name;
39 int type;
40 int param;
41 const char *key;
42 const sval_t *implies_start, *implies_end;
43 func_hook *call_back;
46 static struct ref_func_info func_table[] = {
47 { "clk_prepare", ALLOC, 0, "$", &int_zero, &int_zero },
48 { "clk_prepare_enable", ALLOC, 0, "$", &int_zero, &int_zero },
49 { "clk_disable_unprepare", RELEASE, 0, "$" },
50 { "clk_unprepare", RELEASE, 0, "$" },
52 { "alloc_etherdev_mqs", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
53 { "free_netdev", RELEASE, 0, "$" },
56 * FIXME: A common pattern in release functions like amd76xrom_cleanup()
57 * is to do:
59 * if (window->rsrc.parent)
60 * release_resource(&window->rsrc);
62 * Which is slightly tricky to know how to merge the states so let's
63 * hold off checking request_resource() for now.
65 * { "request_resource", ALLOC, 1, "$", &int_zero, &int_zero },
66 * { "release_resource", RELEASE, 0, "$" },
70 { "pci_request_regions", ALLOC, 0, "$", &int_zero, &int_zero },
71 { "pci_release_regions", RELEASE, 0, "$" },
73 { "request_free_mem_region", ALLOC, -1, "$->start", &valid_ptr_min_sval, &valid_ptr_max_sval },
74 { "__request_region", ALLOC, 1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
75 { "release_and_free_resource", RELEASE, 0, "$->start" },
76 { "release_resource", RELEASE, 0, "$->start" },
77 { "__release_region", RELEASE, 1, "$" },
79 { "ioremap", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
80 { "of_iomap", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
81 { "ioremap_encrypted", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
82 { "iounmap", RELEASE, 0, "$" },
84 { "request_threaded_irq", ALLOC, 0, "$", &int_zero, &int_zero },
85 { "request_irq", ALLOC, 0, "$", &int_zero, &int_zero },
86 { "free_irq", RELEASE, 0, "$" },
87 { "pci_request_irq", ALLOC, 1, "$", &int_zero, &int_zero },
88 { "pci_free_irq", RELEASE, 1, "$" },
90 { "register_netdev", ALLOC, 0, "$", &int_zero, &int_zero },
91 { "unregister_netdev", RELEASE, 0, "$" },
93 { "misc_register", ALLOC, 0, "$", &int_zero, &int_zero },
94 { "misc_deregister", RELEASE, 0, "$" },
96 { "ieee80211_alloc_hw", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
97 { "ieee80211_free_hw", RELEASE, 0, "$" },
99 { "request_firmware", ALLOC, 0, "*$", &int_zero, &int_zero },
100 { "release_firmware", RELEASE, 0, "$" },
103 static struct smatch_state *unmatched_state(struct sm_state *sm)
105 struct smatch_state *state;
107 if (sm->state != &param_released)
108 return &undefined;
110 if (is_impossible_path())
111 return &param_released;
113 state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
114 if (!state)
115 return &undefined;
116 if (!estate_rl(state) || is_err_or_null(estate_rl(state)))
117 return &param_released;
118 if (parent_is_err_or_null_var_sym(sm->name, sm->sym))
119 return &param_released;
121 if (estate_min(state).value == 0 &&
122 estate_max(state).value == 0)
123 return &param_released;
124 if (estate_type(state) == &int_ctype &&
125 sval_is_negative(estate_min(state)) &&
126 (estate_max(state).value == -1 || estate_max(state).value == 0))
127 return &param_released;
129 return &undefined;
132 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
134 if (cur->state == &param_released)
135 return;
136 if (is_impossible_path())
137 set_state(my_id, cur->name, cur->sym, &undefined);
140 static bool is_param_var_sym(const char *name, struct symbol *sym)
142 const char *key;
144 return get_param_key_from_var_sym(name, sym, NULL, &key) >= 0;
147 static bool mark_same_name_as_undefined(const char *name)
149 struct sm_state *sm;
150 int len;
152 if (!name)
153 return false;
155 len = strlen(name);
157 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
158 if (strncmp(sm->name, name, len) != 0)
159 continue;
160 if (sm->name[len] != ':')
161 continue;
162 set_state(sm->owner, sm->name, sm->sym, &unknown);
163 return true;
164 } END_FOR_EACH_SM(sm);
166 return false;
169 static void mark_partial_matches_as_undefined(const char *key)
171 struct sm_state *sm;
172 int start_pos, state_len, key_len;
173 char *p;
175 while ((p = strchr(key, '-'))) {
176 if (p[1] != '>')
177 return;
178 key = p + 2;
180 key_len = strlen(key);
182 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
183 state_len = strlen(sm->name);
184 if (state_len < key_len)
185 continue;
187 start_pos = state_len - key_len;
188 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
189 strcmp(sm->name + start_pos, key) == 0)
190 update_ssa_state(my_id, sm->name, sm->sym, &unknown);
192 } END_FOR_EACH_SM(sm);
195 static void mark_matches_as_undefined(const char *name)
197 if (mark_same_name_as_undefined(name))
198 return;
199 mark_partial_matches_as_undefined(name);
202 static bool is_alloc_primitive(struct expression *expr)
204 int i;
206 while (expr->type == EXPR_ASSIGNMENT)
207 expr = strip_expr(expr->right);
208 if (expr->type != EXPR_CALL)
209 return false;
211 if (expr->fn->type != EXPR_SYMBOL)
212 return false;
214 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
215 if (sym_name_is(func_table[i].name, expr->fn))
216 return true;
219 return false;
222 static void return_param_alloc(struct expression *expr, const char *name, struct symbol *sym, void *data)
224 struct smatch_state *state;
225 char *fn_name;
227 fn_has_alloc = true;
229 if (strncmp(name, "__fake_", 7) == 0)
230 return;
232 if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
233 return;
235 while (expr->type == EXPR_ASSIGNMENT)
236 expr = strip_expr(expr->right);
237 if (expr->type != EXPR_CALL)
238 return;
239 fn_name = expr_to_var(expr->fn);
240 if (!fn_name)
241 return;
243 state = alloc_state_str(fn_name);
244 state->data = &alloc;
246 set_ssa_state(my_id, name, sym, state);
249 static void return_param_release(struct expression *expr, const char *name, struct symbol *sym, void *data)
251 struct sm_state *start_sm;
253 /* The !data means this comes from the DB (not hard coded). */
254 if (!data && is_alloc_primitive(expr))
255 return;
257 start_sm = get_ssa_sm_state(my_id, name, sym);
258 if (start_sm) {
259 update_ssa_sm(my_id, start_sm, &release);
260 } else {
261 if (fn_has_alloc) {
262 mark_matches_as_undefined(name);
263 return;
265 if (is_param_var_sym(name, sym))
266 set_state(info_id, name, sym, &param_released);
270 static void return_param_ignore(struct expression *expr, const char *name, struct symbol *sym, void *data)
272 struct sm_state *start_sm;
274 start_sm = get_ssa_sm_state(my_id, name, sym);
275 if (!start_sm)
276 return;
277 update_ssa_sm(my_id, start_sm, &ignore);
280 static void match_sm_assign(struct sm_state *sm, struct expression *mod_expr)
282 char *left;
283 bool tracked = true;
285 if (!mod_expr || mod_expr->type != EXPR_ASSIGNMENT)
286 return;
288 left = expr_to_str(mod_expr->left);
289 if (!left || strchr(left, '['))
290 tracked = false;
291 free_string(left);
293 if (!tracked)
294 set_state(my_id, sm->name, sm->sym, &ignore);
297 static void unwind_return_info_callback(int return_id, char *return_ranges,
298 struct expression *returned_expr,
299 int param,
300 const char *printed_name,
301 struct sm_state *sm)
303 if (param < 0)
304 return;
305 if (sm->state != &param_released)
306 return;
307 if (is_impossible_path())
308 return;
310 sql_insert_return_states(return_id, return_ranges, RELEASE,
311 param, printed_name, "");
314 static const char *get_alloc_fn(struct sm_state *sm)
316 struct sm_state *tmp;
318 if (slist_has_state(sm->possible, &unknown))
319 return NULL;
321 if (sm->state->data == &alloc)
322 return sm->state->name;
324 FOR_EACH_PTR(sm->possible, tmp) {
325 if (tmp->state->data == &alloc)
326 return tmp->state->name;
327 } END_FOR_EACH_PTR(tmp);
329 return NULL;
332 static void check_balance(const char *name, struct symbol *sym)
334 struct range_list *inc_lines = NULL;
335 int inc_buckets[RET_UNKNOWN + 1] = {};
336 struct stree *stree, *orig_stree;
337 struct smatch_state *state;
338 struct sm_state *return_sm;
339 struct sm_state *sm;
340 sval_t line = sval_type_val(&int_ctype, 0);
341 const char *tmp_name, *fn_name = NULL;
342 int bucket;
344 FOR_EACH_PTR(get_all_return_strees(), stree) {
345 orig_stree = __swap_cur_stree(stree);
347 if (is_impossible_path())
348 goto swap_stree;
349 if (db_incomplete())
350 goto swap_stree;
351 if (has_devm_cleanup())
352 goto swap_stree;
354 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
355 if (!return_sm)
356 goto swap_stree;
357 line.value = return_sm->line;
359 sm = get_sm_state(my_id, name, sym);
360 if (!sm)
361 goto swap_stree;
363 state = sm->state;
364 if (state == &param_released)
365 state = &release;
367 tmp_name = get_alloc_fn(sm);
368 if (tmp_name) {
369 fn_name = tmp_name;
370 state = &alloc;
373 if (state != &alloc &&
374 state != &release)
375 goto swap_stree;
377 bucket = success_fail_return(estate_rl(return_sm->state));
378 if (bucket != RET_FAIL)
379 goto swap_stree;
381 if (state == &alloc) {
382 add_range(&inc_lines, line, line);
383 inc_buckets[bucket] = true;
385 swap_stree:
386 __swap_cur_stree(orig_stree);
387 } END_FOR_EACH_PTR(stree);
389 if (inc_buckets[RET_FAIL])
390 goto complain;
392 return;
394 complain:
395 sm_warning("'%s' from %s() not released on lines: %s.", ssa_name(name), fn_name, show_rl(inc_lines));
398 static void match_check_balanced(struct symbol *sym)
400 struct sm_state *sm;
402 FOR_EACH_MY_SM(my_id, get_all_return_states(), sm) {
403 check_balance(sm->name, sm->sym);
404 } END_FOR_EACH_SM(sm);
407 void check_unwind(int id)
409 struct ref_func_info *info;
410 int i;
412 my_id = id;
414 if (option_project != PROJ_KERNEL)
415 return;
417 set_dynamic_states(my_id);
418 add_pre_merge_hook(my_id, &pre_merge_hook);
420 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
421 param_key_hook *hook;
423 info = &func_table[i];
424 if (info->type == ALLOC)
425 hook = &return_param_alloc;
426 else if (info->type == RELEASE)
427 hook = &return_param_release;
428 else if (info->type == IGNORE)
429 hook = &return_param_ignore;
430 else
431 exit(1);
433 if (info->call_back) {
434 add_function_hook(info->name, info->call_back, info);
435 } else if (info->implies_start && info->type == ALLOC) {
436 return_implies_param_key_exact(info->name,
437 *info->implies_start,
438 *info->implies_end,
439 hook, info->param, info->key, info);
440 } else if (info->implies_start) {
441 return_implies_param_key(info->name,
442 *info->implies_start,
443 *info->implies_end,
444 hook, info->param, info->key, info);
445 } else {
446 add_function_param_key_hook(info->name,
447 hook, info->param, info->key, info);
451 add_ssa_state_assigned_hook(my_id, match_sm_assign);
453 add_function_data(&fn_has_alloc);
455 select_return_param_key(RELEASE, &return_param_release);
456 add_hook(&match_check_balanced, END_FUNC_HOOK);
459 void check_unwind_info(int id)
461 info_id = id;
463 add_unmatched_state_hook(info_id, &unmatched_state);
464 add_return_info_callback(info_id, unwind_return_info_callback);