param_key: fix container of when no struct member is referenced
[smatch.git] / check_unwind.c
blob14fd38829a2d64d235f62179fddb9971f2245a19
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);
34 static unsigned long fn_has_alloc;
36 struct ref_func_info {
37 const char *name;
38 int type;
39 int param;
40 const char *key;
41 const sval_t *implies_start, *implies_end;
42 func_hook *call_back;
45 static struct ref_func_info func_table[] = {
46 { "clk_prepare", ALLOC, 0, "$", &int_zero, &int_zero },
47 { "clk_prepare_enable", ALLOC, 0, "$", &int_zero, &int_zero },
48 { "clk_disable_unprepare", RELEASE, 0, "$" },
49 { "clk_unprepare", RELEASE, 0, "$" },
51 { "alloc_etherdev_mqs", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
52 { "free_netdev", RELEASE, 0, "$" },
55 * FIXME: A common pattern in release functions like amd76xrom_cleanup()
56 * is to do:
58 * if (window->rsrc.parent)
59 * release_resource(&window->rsrc);
61 * Which is slightly tricky to know how to merge the states so let's
62 * hold off checking request_resource() for now.
64 * { "request_resource", ALLOC, 1, "$", &int_zero, &int_zero },
65 * { "release_resource", RELEASE, 0, "$" },
69 { "pci_request_regions", ALLOC, 0, "$", &int_zero, &int_zero },
70 { "pci_release_regions", RELEASE, 0, "$" },
72 { "request_free_mem_region", ALLOC, -1, "$->start", &valid_ptr_min_sval, &valid_ptr_max_sval },
73 { "__request_region", ALLOC, 1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
74 { "release_and_free_resource", RELEASE, 0, "$->start" },
75 { "release_resource", RELEASE, 0, "$->start" },
76 { "__release_region", RELEASE, 1, "$" },
78 { "ioremap", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
79 { "of_iomap", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
80 { "ioremap_encrypted", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
81 { "iounmap", RELEASE, 0, "$" },
83 { "request_threaded_irq", ALLOC, 0, "$", &int_zero, &int_zero },
84 { "request_irq", ALLOC, 0, "$", &int_zero, &int_zero },
85 { "free_irq", RELEASE, 0, "$" },
86 { "pci_request_irq", ALLOC, 1, "$", &int_zero, &int_zero },
87 { "pci_free_irq", RELEASE, 1, "$" },
89 { "register_netdev", ALLOC, 0, "$", &int_zero, &int_zero },
90 { "unregister_netdev", RELEASE, 0, "$" },
92 { "misc_register", ALLOC, 0, "$", &int_zero, &int_zero },
93 { "misc_deregister", RELEASE, 0, "$" },
95 { "ieee80211_alloc_hw", ALLOC, -1, "$", &valid_ptr_min_sval, &valid_ptr_max_sval },
96 { "ieee80211_free_hw", RELEASE, 0, "$" },
99 static struct smatch_state *unmatched_state(struct sm_state *sm)
101 struct smatch_state *state;
103 if (sm->state != &param_released)
104 return &undefined;
106 if (is_impossible_path())
107 return &param_released;
109 state = get_state(SMATCH_EXTRA, sm->name, sm->sym);
110 if (!state)
111 return &undefined;
112 if (!estate_rl(state) || is_err_or_null(estate_rl(state)))
113 return &param_released;
114 if (parent_is_err_or_null_var_sym(sm->name, sm->sym))
115 return &param_released;
117 if (estate_min(state).value == 0 &&
118 estate_max(state).value == 0)
119 return &param_released;
120 if (estate_type(state) == &int_ctype &&
121 sval_is_negative(estate_min(state)) &&
122 (estate_max(state).value == -1 || estate_max(state).value == 0))
123 return &param_released;
125 return &undefined;
128 static bool is_param_var_sym(const char *name, struct symbol *sym)
130 const char *key;
132 return get_param_key_from_var_sym(name, sym, NULL, &key) >= 0;
135 static void mark_matches_as_undefined(const char *key)
137 struct sm_state *sm;
138 int start_pos, state_len, key_len;
139 char *p;
141 while ((p = strchr(key, '-'))) {
142 if (p[1] != '>')
143 return;
144 key = p + 2;
146 key_len = strlen(key);
148 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
149 state_len = strlen(sm->name);
150 if (state_len < key_len)
151 continue;
153 start_pos = state_len - key_len;
154 if ((start_pos == 0 || !isalnum(sm->name[start_pos - 1])) &&
155 strcmp(sm->name + start_pos, key) == 0)
156 update_ssa_state(my_id, sm->name, sm->sym, &undefined);
158 } END_FOR_EACH_SM(sm);
161 static bool is_alloc_primitive(struct expression *expr)
163 int i;
165 while (expr->type == EXPR_ASSIGNMENT)
166 expr = strip_expr(expr->right);
167 if (expr->type != EXPR_CALL)
168 return false;
170 if (expr->fn->type != EXPR_SYMBOL)
171 return false;
173 for (i = 0; i < ARRAY_SIZE(func_table); i++) {
174 if (sym_name_is(func_table[i].name, expr->fn))
175 return true;
178 return false;
181 static void return_param_alloc(struct expression *expr, const char *name, struct symbol *sym, void *data)
183 struct smatch_state *state;
184 char *fn_name;
186 fn_has_alloc = true;
188 if (strncmp(name, "__fake_", 7) == 0)
189 return;
191 while (expr->type == EXPR_ASSIGNMENT)
192 expr = strip_expr(expr->right);
193 if (expr->type != EXPR_CALL)
194 return;
195 fn_name = expr_to_var(expr->fn);
196 if (!fn_name)
197 return;
199 state = alloc_state_str(fn_name);
200 state->data = &alloc;
202 set_ssa_state(my_id, name, sym, state);
205 static void return_param_release(struct expression *expr, const char *name, struct symbol *sym, void *data)
207 struct sm_state *start_sm;
209 /* The !data means this comes from the DB (not hard coded). */
210 if (!data && is_alloc_primitive(expr))
211 return;
213 start_sm = get_ssa_sm_state(my_id, name, sym);
214 if (start_sm) {
215 update_ssa_sm(my_id, start_sm, &release);
216 } else {
217 if (fn_has_alloc) {
218 mark_matches_as_undefined(name);
219 return;
221 if (is_param_var_sym(name, sym))
222 set_state(info_id, name, sym, &param_released);
226 static void return_param_ignore(struct expression *expr, const char *name, struct symbol *sym, void *data)
228 struct sm_state *start_sm;
230 start_sm = get_ssa_sm_state(my_id, name, sym);
231 if (!start_sm)
232 return;
233 update_ssa_sm(my_id, start_sm, &ignore);
236 static void ignore_path(const char *fn, struct expression *expr, void *data)
238 set_state(my_id, "path", NULL, &ignore);
241 static void match_sm_assign(struct sm_state *sm, struct expression *mod_expr)
243 char *left;
244 bool tracked = true;
246 if (!mod_expr || mod_expr->type != EXPR_ASSIGNMENT)
247 return;
249 left = expr_to_str(mod_expr->left);
250 if (!left || strchr(left, '['))
251 tracked = false;
252 free_string(left);
254 if (!tracked)
255 set_state(my_id, sm->name, sm->sym, &ignore);
258 static void unwind_return_info_callback(int return_id, char *return_ranges,
259 struct expression *returned_expr,
260 int param,
261 const char *printed_name,
262 struct sm_state *sm)
264 if (param < 0)
265 return;
266 if (sm->state != &param_released)
267 return;
268 if (is_impossible_path())
269 return;
271 sql_insert_return_states(return_id, return_ranges, RELEASE,
272 param, printed_name, "");
275 enum {
276 UNKNOWN, FAIL, SUCCESS, NUM_BUCKETS
279 static int success_fail_positive(struct range_list *rl)
281 sval_t sval;
283 if (!rl)
284 return SUCCESS;
286 // Negatives are a failure
287 if (sval_is_negative(rl_min(rl)) && sval_is_negative(rl_max(rl)))
288 return FAIL;
290 // NULL and error pointers are a failure
291 if (type_is_ptr(rl_type(rl)) && is_err_or_null(rl))
292 return FAIL;
294 if (rl_to_sval(rl, &sval)) {
295 if (sval.value == 0) {
296 // Zero is normally success but false is a failure
297 if (type_bits(sval.type) == 1)
298 return FAIL;
299 else
300 return SUCCESS;
302 // true is success
303 if (sval.value == 1 && type_bits(sval.type) == 1)
304 return SUCCESS;
307 return UNKNOWN;
310 static const char *get_alloc_fn(struct sm_state *sm)
312 struct sm_state *tmp;
313 const char *alloc_fn = NULL;
314 bool released = false;
316 if (sm->state->data == &alloc)
317 return sm->state->name;
319 FOR_EACH_PTR(sm->possible, tmp) {
320 if (tmp->state->data == &alloc)
321 alloc_fn = tmp->state->name;
322 if (tmp->state == &release)
323 released = true;
324 } END_FOR_EACH_PTR(tmp);
326 if (alloc_fn && !released)
327 return alloc_fn;
328 return NULL;
331 static void check_balance(const char *name, struct symbol *sym)
333 struct range_list *inc_lines = NULL;
334 int inc_buckets[NUM_BUCKETS] = {};
335 struct stree *stree, *orig_stree;
336 struct smatch_state *state;
337 struct sm_state *return_sm;
338 struct sm_state *sm;
339 sval_t line = sval_type_val(&int_ctype, 0);
340 const char *tmp_name, *fn_name = NULL;
341 int bucket;
343 FOR_EACH_PTR(get_all_return_strees(), stree) {
344 orig_stree = __swap_cur_stree(stree);
346 if (is_impossible_path())
347 goto swap_stree;
348 if (db_incomplete())
349 goto swap_stree;
350 if (get_state(my_id, "path", NULL) == &ignore)
351 goto swap_stree;
353 return_sm = get_sm_state(RETURN_ID, "return_ranges", NULL);
354 if (!return_sm)
355 goto swap_stree;
356 line.value = return_sm->line;
358 sm = get_sm_state(my_id, name, sym);
359 if (!sm)
360 goto swap_stree;
362 state = sm->state;
363 if (state == &param_released)
364 state = &release;
366 tmp_name = get_alloc_fn(sm);
367 if (tmp_name) {
368 fn_name = tmp_name;
369 state = &alloc;
372 if (state != &alloc &&
373 state != &release)
374 goto swap_stree;
376 bucket = success_fail_positive(estate_rl(return_sm->state));
377 if (bucket != FAIL)
378 goto swap_stree;
380 if (state == &alloc) {
381 add_range(&inc_lines, line, line);
382 inc_buckets[bucket] = true;
384 swap_stree:
385 __swap_cur_stree(orig_stree);
386 } END_FOR_EACH_PTR(stree);
388 if (inc_buckets[FAIL])
389 goto complain;
391 return;
393 complain:
394 sm_warning("'%s' from %s() not released on lines: %s.", ssa_name(name), fn_name, show_rl(inc_lines));
397 static void match_check_balanced(struct symbol *sym)
399 struct sm_state *sm;
401 FOR_EACH_MY_SM(my_id, get_all_return_states(), sm) {
402 if (sm->sym == NULL && strcmp(sm->name, "path") == 0)
403 continue;
404 check_balance(sm->name, sm->sym);
405 } END_FOR_EACH_SM(sm);
408 void check_unwind(int id)
410 struct ref_func_info *info;
411 int i;
413 my_id = id;
415 if (option_project != PROJ_KERNEL)
416 return;
418 set_dynamic_states(my_id);
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_function_hook("devm_add_action_or_reset", &ignore_path, NULL);
452 add_function_hook("drmm_add_action", &ignore_path, NULL);
453 add_function_hook("__drmm_add_action", &ignore_path, NULL);
454 add_function_hook("pcim_enable_device", &ignore_path, NULL);
455 add_function_hook("pci_enable_device", &ignore_path, NULL);
456 add_function_hook("put_device", &ignore_path, NULL);
458 add_ssa_state_assigned_hook(my_id, match_sm_assign);
460 add_function_data(&fn_has_alloc);
462 select_return_param_key(RELEASE, &return_param_release);
463 select_return_param_key(RELEASE_OLD, &return_param_release);
464 add_hook(&match_check_balanced, END_FUNC_HOOK);
467 void check_unwind_info(int id)
469 info_id = id;
471 add_unmatched_state_hook(info_id, &unmatched_state);
472 add_return_info_callback(info_id, unwind_return_info_callback);