unwind: separate path states out into a different check id
[smatch.git] / check_unwind.c
blobc2f60f4254f6011a601289955d2cfeee46d46c7d
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 path_id;
26 static int info_id;
28 #define IGNORE 100000
30 STATE(alloc);
31 STATE(release);
32 STATE(param_released);
33 STATE(ignore);
34 STATE(unknown);
36 static unsigned long fn_has_alloc;
38 struct ref_func_info {
39 const char *name;
40 int type;
41 int param;
42 const char *key;
43 const sval_t *implies_start, *implies_end;
44 func_hook *call_back;
47 static struct ref_func_info func_table[] = {
48 { "clk_prepare", ALLOC, 0, "$", &int_zero, &int_zero },
49 { "clk_prepare_enable", ALLOC, 0, "$", &int_zero, &int_zero },
50 { "clk_disable_unprepare", RELEASE, 0, "$" },
51 { "clk_unprepare", RELEASE, 0, "$" },
53 { "alloc_etherdev_mqs", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
54 { "free_netdev", RELEASE, 0, "$" },
57 * FIXME: A common pattern in release functions like amd76xrom_cleanup()
58 * is to do:
60 * if (window->rsrc.parent)
61 * release_resource(&window->rsrc);
63 * Which is slightly tricky to know how to merge the states so let's
64 * hold off checking request_resource() for now.
66 * { "request_resource", ALLOC, 1, "$", &int_zero, &int_zero },
67 * { "release_resource", RELEASE, 0, "$" },
71 { "pci_request_regions", ALLOC, 0, "$", &int_zero, &int_zero },
72 { "pci_release_regions", RELEASE, 0, "$" },
74 { "request_free_mem_region", ALLOC, -1, "$->start", &valid_ptr_min_sval, &valid_ptr_max_sval },
75 { "__request_region", ALLOC, 1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
76 { "release_and_free_resource", RELEASE, 0, "$->start" },
77 { "release_resource", RELEASE, 0, "$->start" },
78 { "__release_region", RELEASE, 1, "$" },
80 { "ioremap", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
81 { "of_iomap", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
82 { "ioremap_encrypted", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
83 { "iounmap", RELEASE, 0, "$" },
85 { "request_threaded_irq", ALLOC, 0, "$", &int_zero, &int_zero },
86 { "request_irq", ALLOC, 0, "$", &int_zero, &int_zero },
87 { "free_irq", RELEASE, 0, "$" },
88 { "pci_request_irq", ALLOC, 1, "$", &int_zero, &int_zero },
89 { "pci_free_irq", RELEASE, 1, "$" },
91 { "register_netdev", ALLOC, 0, "$", &int_zero, &int_zero },
92 { "unregister_netdev", RELEASE, 0, "$" },
94 { "misc_register", ALLOC, 0, "$", &int_zero, &int_zero },
95 { "misc_deregister", RELEASE, 0, "$" },
97 { "ieee80211_alloc_hw", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
98 { "ieee80211_free_hw", RELEASE, 0, "$" },
101 static struct smatch_state *unmatched_state(struct sm_state *sm)
103 struct smatch_state *state;
105 if (sm->state != &param_released)
106 return &undefined;
108 if (is_impossible_path())
109 return &param_released;
111 state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
112 if (!state)
113 return &undefined;
114 if (!estate_rl(state) || is_err_or_null(estate_rl(state)))
115 return &param_released;
116 if (parent_is_err_or_null_var_sym(sm->name, sm->sym))
117 return &param_released;
119 if (estate_min(state).value == 0 &&
120 estate_max(state).value == 0)
121 return &param_released;
122 if (estate_type(state) == &int_ctype &&
123 sval_is_negative(estate_min(state)) &&
124 (estate_max(state).value == -1 || estate_max(state).value == 0))
125 return &param_released;
127 return &undefined;
130 static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
132 if (cur->state == &param_released)
133 return;
134 if (is_impossible_path())
135 set_state(my_id, cur->name, cur->sym, &undefined);
138 static bool is_param_var_sym(const char *name, struct symbol *sym)
140 const char *key;
142 return get_param_key_from_var_sym(name, sym, NULL, &key) >= 0;
145 static bool mark_same_name_as_undefined(const char *name)
147 struct sm_state *sm;
148 int len;
150 if (!name)
151 return false;
153 len = strlen(name);
155 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
156 if (strncmp(sm->name, name, len) != 0)
157 continue;
158 if (sm->name[len] != ':')
159 continue;
160 set_state(sm->owner, sm->name, sm->sym, &unknown);
161 return true;
162 } END_FOR_EACH_SM(sm);
164 return false;
167 static void mark_partial_matches_as_undefined(const char *key)
169 struct sm_state *sm;
170 int start_pos, state_len, key_len;
171 char *p;
173 while ((p = strchr(key, '-'))) {
174 if (p[1] != '>')
175 return;
176 key = p + 2;
178 key_len = strlen(key);
180 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
181 state_len = strlen(sm->name);
182 if (state_len < key_len)
183 continue;
185 start_pos = state_len - key_len;
186 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
187 strcmp(sm->name + start_pos, key) == 0)
188 update_ssa_state(my_id, sm->name, sm->sym, &unknown);
190 } END_FOR_EACH_SM(sm);
193 static void mark_matches_as_undefined(const char *name)
195 if (mark_same_name_as_undefined(name))
196 return;
197 mark_partial_matches_as_undefined(name);
200 static bool is_alloc_primitive(struct expression *expr)
202 int i;
204 while (expr->type == EXPR_ASSIGNMENT)
205 expr = strip_expr(expr->right);
206 if (expr->type != EXPR_CALL)
207 return false;
209 if (expr->fn->type != EXPR_SYMBOL)
210 return false;
212 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
213 if (sym_name_is(func_table[i].name, expr->fn))
214 return true;
217 return false;
220 static void return_param_alloc(struct expression *expr, const char *name, struct symbol *sym, void *data)
222 struct smatch_state *state;
223 char *fn_name;
225 fn_has_alloc = true;
227 if (strncmp(name, "__fake_", 7) == 0)
228 return;
230 if (sym->ctype.modifiers & (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE))
231 return;
233 while (expr->type == EXPR_ASSIGNMENT)
234 expr = strip_expr(expr->right);
235 if (expr->type != EXPR_CALL)
236 return;
237 fn_name = expr_to_var(expr->fn);
238 if (!fn_name)
239 return;
241 state = alloc_state_str(fn_name);
242 state->data = &alloc;
244 set_ssa_state(my_id, name, sym, state);
247 static void return_param_release(struct expression *expr, const char *name, struct symbol *sym, void *data)
249 struct sm_state *start_sm;
251 /* The !data means this comes from the DB (not hard coded). */
252 if (!data && is_alloc_primitive(expr))
253 return;
255 start_sm = get_ssa_sm_state(my_id, name, sym);
256 if (start_sm) {
257 update_ssa_sm(my_id, start_sm, &release);
258 } else {
259 if (fn_has_alloc) {
260 mark_matches_as_undefined(name);
261 return;
263 if (is_param_var_sym(name, sym))
264 set_state(info_id, name, sym, &param_released);
268 static void return_param_ignore(struct expression *expr, const char *name, struct symbol *sym, void *data)
270 struct sm_state *start_sm;
272 start_sm = get_ssa_sm_state(my_id, name, sym);
273 if (!start_sm)
274 return;
275 update_ssa_sm(my_id, start_sm, &ignore);
278 static void match_sm_assign(struct sm_state *sm, struct expression *mod_expr)
280 char *left;
281 bool tracked = true;
283 if (!mod_expr || mod_expr->type != EXPR_ASSIGNMENT)
284 return;
286 left = expr_to_str(mod_expr->left);
287 if (!left || strchr(left, '['))
288 tracked = false;
289 free_string(left);
291 if (!tracked)
292 set_state(my_id, sm->name, sm->sym, &ignore);
295 static void unwind_return_info_callback(int return_id, char *return_ranges,
296 struct expression *returned_expr,
297 int param,
298 const char *printed_name,
299 struct sm_state *sm)
301 if (param < 0)
302 return;
303 if (sm->state != &param_released)
304 return;
305 if (is_impossible_path())
306 return;
308 sql_insert_return_states(return_id, return_ranges, RELEASE,
309 param, printed_name, "");
312 static const char *get_alloc_fn(struct sm_state *sm)
314 struct sm_state *tmp;
316 if (slist_has_state(sm->possible, &unknown))
317 return NULL;
319 if (sm->state->data == &alloc)
320 return sm->state->name;
322 FOR_EACH_PTR(sm->possible, tmp) {
323 if (tmp->state->data == &alloc)
324 return tmp->state->name;
325 } END_FOR_EACH_PTR(tmp);
327 return NULL;
330 static void check_balance(const char *name, struct symbol *sym)
332 struct range_list *inc_lines = NULL;
333 int inc_buckets[RET_UNKNOWN + 1] = {};
334 struct stree *stree, *orig_stree;
335 struct smatch_state *state;
336 struct sm_state *return_sm;
337 struct sm_state *sm;
338 sval_t line = sval_type_val(&int_ctype, 0);
339 const char *tmp_name, *fn_name = NULL;
340 int bucket;
342 FOR_EACH_PTR(get_all_return_strees(), stree) {
343 orig_stree = __swap_cur_stree(stree);
345 if (is_impossible_path())
346 goto swap_stree;
347 if (db_incomplete())
348 goto swap_stree;
349 if (get_state(path_id, "path", NULL) == &ignore)
350 goto swap_stree;
352 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
353 if (!return_sm)
354 goto swap_stree;
355 line.value = return_sm->line;
357 sm = get_sm_state(my_id, name, sym);
358 if (!sm)
359 goto swap_stree;
361 state = sm->state;
362 if (state == &param_released)
363 state = &release;
365 tmp_name = get_alloc_fn(sm);
366 if (tmp_name) {
367 fn_name = tmp_name;
368 state = &alloc;
371 if (state != &alloc &&
372 state != &release)
373 goto swap_stree;
375 bucket = success_fail_return(estate_rl(return_sm->state));
376 if (bucket != RET_FAIL)
377 goto swap_stree;
379 if (state == &alloc) {
380 add_range(&inc_lines, line, line);
381 inc_buckets[bucket] = true;
383 swap_stree:
384 __swap_cur_stree(orig_stree);
385 } END_FOR_EACH_PTR(stree);
387 if (inc_buckets[RET_FAIL])
388 goto complain;
390 return;
392 complain:
393 sm_warning("'%s' from %s() not released on lines: %s.", ssa_name(name), fn_name, show_rl(inc_lines));
396 static void match_check_balanced(struct symbol *sym)
398 struct sm_state *sm;
400 FOR_EACH_MY_SM(my_id, get_all_return_states(), sm) {
401 check_balance(sm->name, sm->sym);
402 } END_FOR_EACH_SM(sm);
405 void check_unwind(int id)
407 struct ref_func_info *info;
408 int i;
410 my_id = id;
412 if (option_project != PROJ_KERNEL)
413 return;
415 set_dynamic_states(my_id);
416 add_pre_merge_hook(my_id, &pre_merge_hook);
418 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
419 param_key_hook *hook;
421 info = &func_table[i];
422 if (info->type == ALLOC)
423 hook = &return_param_alloc;
424 else if (info->type == RELEASE)
425 hook = &return_param_release;
426 else if (info->type == IGNORE)
427 hook = &return_param_ignore;
428 else
429 exit(1);
431 if (info->call_back) {
432 add_function_hook(info->name, info->call_back, info);
433 } else if (info->implies_start && info->type == ALLOC) {
434 return_implies_param_key_exact(info->name,
435 *info->implies_start,
436 *info->implies_end,
437 hook, info->param, info->key, info);
438 } else if (info->implies_start) {
439 return_implies_param_key(info->name,
440 *info->implies_start,
441 *info->implies_end,
442 hook, info->param, info->key, info);
443 } else {
444 add_function_param_key_hook(info->name,
445 hook, info->param, info->key, info);
449 add_ssa_state_assigned_hook(my_id, match_sm_assign);
451 add_function_data(&fn_has_alloc);
453 select_return_param_key(RELEASE, &return_param_release);
454 add_hook(&match_check_balanced, END_FUNC_HOOK);
457 static void ignore_path(const char *fn, struct expression *expr, void *data)
459 set_state(path_id, "path", NULL, &ignore);
462 void check_unwind_path(int id)
464 path_id = id;
466 add_function_hook("devm_add_action_or_reset", &ignore_path, NULL);
467 add_function_hook("drmm_add_action", &ignore_path, NULL);
468 add_function_hook("__drmm_add_action", &ignore_path, NULL);
469 add_function_hook("pcim_enable_device", &ignore_path, NULL);
470 add_function_hook("pci_enable_device", &ignore_path, NULL);
471 add_function_hook("put_device", &ignore_path, NULL);
472 add_function_hook("component_match_add_release", &ignore_path, NULL);
475 void check_unwind_info(int id)
477 info_id = id;
479 add_unmatched_state_hook(info_id, &unmatched_state);
480 add_return_info_callback(info_id, unwind_return_info_callback);