11f5207b7SJohn Levon /* 21f5207b7SJohn Levon * Copyright (C) 2010 Dan Carpenter. 31f5207b7SJohn Levon * 41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or 51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License 61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2 71f5207b7SJohn Levon * of the License, or (at your option) any later version. 81f5207b7SJohn Levon * 91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful, 101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of 111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 121f5207b7SJohn Levon * GNU General Public License for more details. 131f5207b7SJohn Levon * 141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License 151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 161f5207b7SJohn Levon */ 171f5207b7SJohn Levon 181f5207b7SJohn Levon /* 191f5207b7SJohn Levon * This is a kernel check to make sure we unwind everything on 201f5207b7SJohn Levon * on errors. 211f5207b7SJohn Levon * 221f5207b7SJohn Levon */ 231f5207b7SJohn Levon 241f5207b7SJohn Levon #include "smatch.h" 251f5207b7SJohn Levon #include "smatch_extra.h" 261f5207b7SJohn Levon #include "smatch_slist.h" 271f5207b7SJohn Levon 281f5207b7SJohn Levon #define EBUSY 16 291f5207b7SJohn Levon #define MAX_ERRNO 4095 301f5207b7SJohn Levon 311f5207b7SJohn Levon static int my_id; 321f5207b7SJohn Levon 331f5207b7SJohn Levon STATE(allocated); 341f5207b7SJohn Levon STATE(unallocated); 351f5207b7SJohn Levon 361f5207b7SJohn Levon /* state of unwind function */ 371f5207b7SJohn Levon STATE(called); 381f5207b7SJohn Levon 391f5207b7SJohn Levon static int was_passed_as_param(struct expression *expr) 401f5207b7SJohn Levon { 411f5207b7SJohn Levon char *name; 421f5207b7SJohn Levon struct symbol *sym; 431f5207b7SJohn Levon struct symbol *arg; 441f5207b7SJohn Levon 451f5207b7SJohn Levon name = expr_to_var_sym(expr, &sym); 461f5207b7SJohn Levon if (!name) 471f5207b7SJohn Levon return 0; 481f5207b7SJohn Levon free_string(name); 491f5207b7SJohn Levon 501f5207b7SJohn Levon FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) { 511f5207b7SJohn Levon if (arg == sym) 521f5207b7SJohn Levon return 1; 531f5207b7SJohn Levon } END_FOR_EACH_PTR(arg); 541f5207b7SJohn Levon return 0; 551f5207b7SJohn Levon } 561f5207b7SJohn Levon 571f5207b7SJohn Levon static void print_unwind_functions(const char *fn, struct expression *expr, void *_arg_no) 581f5207b7SJohn Levon { 591f5207b7SJohn Levon struct expression *arg_expr; 601f5207b7SJohn Levon int arg_no = PTR_INT(_arg_no); 611f5207b7SJohn Levon static struct symbol *last_printed = NULL; 621f5207b7SJohn Levon 631f5207b7SJohn Levon arg_expr = get_argument_from_call_expr(expr->args, arg_no); 641f5207b7SJohn Levon if (!was_passed_as_param(arg_expr)) 651f5207b7SJohn Levon return; 661f5207b7SJohn Levon if (last_printed == cur_func_sym) 671f5207b7SJohn Levon return; 681f5207b7SJohn Levon last_printed = cur_func_sym; 691f5207b7SJohn Levon sm_msg("info: is unwind function"); 701f5207b7SJohn Levon } 711f5207b7SJohn Levon 721f5207b7SJohn Levon static void request_granted(const char *fn, struct expression *call_expr, 731f5207b7SJohn Levon struct expression *assign_expr, void *_arg_no) 741f5207b7SJohn Levon { 751f5207b7SJohn Levon struct expression *arg_expr; 761f5207b7SJohn Levon int arg_no = PTR_INT(_arg_no); 771f5207b7SJohn Levon 781f5207b7SJohn Levon if (arg_no == -1) { 791f5207b7SJohn Levon if (!assign_expr) 801f5207b7SJohn Levon return; 811f5207b7SJohn Levon arg_expr = assign_expr->left; 821f5207b7SJohn Levon } else { 831f5207b7SJohn Levon arg_expr = get_argument_from_call_expr(call_expr->args, arg_no); 841f5207b7SJohn Levon } 851f5207b7SJohn Levon set_state_expr(my_id, arg_expr, &allocated); 861f5207b7SJohn Levon } 871f5207b7SJohn Levon 881f5207b7SJohn Levon static void request_denied(const char *fn, struct expression *call_expr, 891f5207b7SJohn Levon struct expression *assign_expr, void *_arg_no) 901f5207b7SJohn Levon { 911f5207b7SJohn Levon struct expression *arg_expr; 921f5207b7SJohn Levon int arg_no = PTR_INT(_arg_no); 931f5207b7SJohn Levon 941f5207b7SJohn Levon if (arg_no == -1) { 951f5207b7SJohn Levon if (!assign_expr) 961f5207b7SJohn Levon return; 971f5207b7SJohn Levon arg_expr = assign_expr->left; 981f5207b7SJohn Levon } else { 991f5207b7SJohn Levon arg_expr = get_argument_from_call_expr(call_expr->args, arg_no); 1001f5207b7SJohn Levon } 1011f5207b7SJohn Levon set_state_expr(my_id, arg_expr, &unallocated); 1021f5207b7SJohn Levon } 1031f5207b7SJohn Levon 1041f5207b7SJohn Levon static void match_release(const char *fn, struct expression *expr, void *_arg_no) 1051f5207b7SJohn Levon { 1061f5207b7SJohn Levon struct expression *arg_expr; 1071f5207b7SJohn Levon int arg_no = PTR_INT(_arg_no); 1081f5207b7SJohn Levon 1091f5207b7SJohn Levon arg_expr = get_argument_from_call_expr(expr->args, arg_no); 1101f5207b7SJohn Levon if (get_state_expr(my_id, arg_expr)) 1111f5207b7SJohn Levon set_state_expr(my_id, arg_expr, &unallocated); 1121f5207b7SJohn Levon set_equiv_state_expr(my_id, arg_expr, &unallocated); 1131f5207b7SJohn Levon } 1141f5207b7SJohn Levon 1151f5207b7SJohn Levon static void match_unwind_function(const char *fn, struct expression *expr, void *unused) 1161f5207b7SJohn Levon { 1171f5207b7SJohn Levon set_state(my_id, "unwind_function", NULL, &called); 1181f5207b7SJohn Levon } 1191f5207b7SJohn Levon 1201f5207b7SJohn Levon static int func_returns_int(void) 1211f5207b7SJohn Levon { 1221f5207b7SJohn Levon struct symbol *type; 1231f5207b7SJohn Levon 1241f5207b7SJohn Levon type = get_base_type(cur_func_sym); 1251f5207b7SJohn Levon if (!type || type->type != SYM_FN) 1261f5207b7SJohn Levon return 0; 1271f5207b7SJohn Levon type = get_base_type(type); 1281f5207b7SJohn Levon if (type->ctype.base_type == &int_type) { 1291f5207b7SJohn Levon return 1; 1301f5207b7SJohn Levon } 1311f5207b7SJohn Levon return 0; 1321f5207b7SJohn Levon } 1331f5207b7SJohn Levon 1341f5207b7SJohn Levon static void match_return(struct expression *ret_value) 1351f5207b7SJohn Levon { 1361f5207b7SJohn Levon struct stree *stree; 1371f5207b7SJohn Levon struct sm_state *tmp; 1381f5207b7SJohn Levon sval_t sval; 1391f5207b7SJohn Levon 1401f5207b7SJohn Levon if (!func_returns_int()) 1411f5207b7SJohn Levon return; 1421f5207b7SJohn Levon if (get_value(ret_value, &sval) && sval_cmp_val(sval, 0) >= 0) 1431f5207b7SJohn Levon return; 1441f5207b7SJohn Levon if (!implied_not_equal(ret_value, 0)) 1451f5207b7SJohn Levon return; 1461f5207b7SJohn Levon if (get_state(my_id, "unwind_function", NULL) == &called) 1471f5207b7SJohn Levon return; 1481f5207b7SJohn Levon 1491f5207b7SJohn Levon stree = __get_cur_stree(); 1501f5207b7SJohn Levon FOR_EACH_MY_SM(my_id, stree, tmp) { 1511f5207b7SJohn Levon if (slist_has_state(tmp->possible, &allocated)) 1521f5207b7SJohn Levon sm_warning("'%s' was not released on error", tmp->name); 1531f5207b7SJohn Levon } END_FOR_EACH_SM(tmp); 1541f5207b7SJohn Levon } 1551f5207b7SJohn Levon 1561f5207b7SJohn Levon static void register_unwind_functions(void) 1571f5207b7SJohn Levon { 1581f5207b7SJohn Levon struct token *token; 1591f5207b7SJohn Levon const char *func; 1601f5207b7SJohn Levon 1611f5207b7SJohn Levon token = get_tokens_file("kernel.unwind_functions"); 1621f5207b7SJohn Levon if (!token) 1631f5207b7SJohn Levon return; 1641f5207b7SJohn Levon if (token_type(token) != TOKEN_STREAMBEGIN) 1651f5207b7SJohn Levon return; 1661f5207b7SJohn Levon token = token->next; 1671f5207b7SJohn Levon while (token_type(token) != TOKEN_STREAMEND) { 1681f5207b7SJohn Levon if (token_type(token) != TOKEN_IDENT) 1691f5207b7SJohn Levon return; 1701f5207b7SJohn Levon func = show_ident(token->ident); 1711f5207b7SJohn Levon add_function_hook(func, &match_unwind_function, NULL); 1721f5207b7SJohn Levon token = token->next; 1731f5207b7SJohn Levon } 1741f5207b7SJohn Levon clear_token_alloc(); 1751f5207b7SJohn Levon } 1761f5207b7SJohn Levon 1771f5207b7SJohn Levon static void release_function_indicator(const char *name) 1781f5207b7SJohn Levon { 1791f5207b7SJohn Levon if (!option_info) 1801f5207b7SJohn Levon return; 1811f5207b7SJohn Levon add_function_hook(name, &print_unwind_functions, INT_PTR(0)); 1821f5207b7SJohn Levon } 1831f5207b7SJohn Levon 1841f5207b7SJohn Levon void check_unwind(int id) 1851f5207b7SJohn Levon { 1861f5207b7SJohn Levon if (option_project != PROJ_KERNEL || !option_spammy) 1871f5207b7SJohn Levon return; 1881f5207b7SJohn Levon my_id = id; 1891f5207b7SJohn Levon 1901f5207b7SJohn Levon register_unwind_functions(); 1911f5207b7SJohn Levon 1921f5207b7SJohn Levon return_implies_state("request_resource", 0, 0, &request_granted, INT_PTR(1)); 1931f5207b7SJohn Levon return_implies_state("request_resource", -EBUSY, -EBUSY, &request_denied, INT_PTR(1)); 1941f5207b7SJohn Levon add_function_hook("release_resource", &match_release, INT_PTR(0)); 1951f5207b7SJohn Levon release_function_indicator("release_resource"); 1961f5207b7SJohn Levon 197*efe51d0cSJohn Levon return_implies_state_sval("__request_region", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, INT_PTR(1)); 1981f5207b7SJohn Levon return_implies_state("__request_region", 0, 0, &request_denied, INT_PTR(1)); 1991f5207b7SJohn Levon add_function_hook("__release_region", &match_release, INT_PTR(1)); 2001f5207b7SJohn Levon release_function_indicator("__release_region"); 2011f5207b7SJohn Levon 202*efe51d0cSJohn Levon return_implies_state_sval("ioremap", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, INT_PTR(-1)); 2031f5207b7SJohn Levon return_implies_state("ioremap", 0, 0, &request_denied, INT_PTR(-1)); 2041f5207b7SJohn Levon add_function_hook("iounmap", &match_release, INT_PTR(0)); 2051f5207b7SJohn Levon 206*efe51d0cSJohn Levon return_implies_state_sval("pci_iomap", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, INT_PTR(-1)); 2071f5207b7SJohn Levon return_implies_state("pci_iomap", 0, 0, &request_denied, INT_PTR(-1)); 2081f5207b7SJohn Levon add_function_hook("pci_iounmap", &match_release, INT_PTR(1)); 2091f5207b7SJohn Levon release_function_indicator("pci_iounmap"); 2101f5207b7SJohn Levon 211*efe51d0cSJohn Levon return_implies_state_sval("__create_workqueue_key", valid_ptr_min_sval, valid_ptr_max_sval, &request_granted, 2121f5207b7SJohn Levon INT_PTR(-1)); 2131f5207b7SJohn Levon return_implies_state("__create_workqueue_key", 0, 0, &request_denied, INT_PTR(-1)); 2141f5207b7SJohn Levon add_function_hook("destroy_workqueue", &match_release, INT_PTR(0)); 2151f5207b7SJohn Levon 2161f5207b7SJohn Levon return_implies_state("request_irq", 0, 0, &request_granted, INT_PTR(0)); 2171f5207b7SJohn Levon return_implies_state("request_irq", -MAX_ERRNO, -1, &request_denied, INT_PTR(0)); 2181f5207b7SJohn Levon add_function_hook("free_irq", &match_release, INT_PTR(0)); 2191f5207b7SJohn Levon release_function_indicator("free_irq"); 2201f5207b7SJohn Levon 2211f5207b7SJohn Levon return_implies_state("register_netdev", 0, 0, &request_granted, INT_PTR(0)); 2221f5207b7SJohn Levon return_implies_state("register_netdev", -MAX_ERRNO, -1, &request_denied, INT_PTR(0)); 2231f5207b7SJohn Levon add_function_hook("unregister_netdev", &match_release, INT_PTR(0)); 2241f5207b7SJohn Levon release_function_indicator("unregister_netdev"); 2251f5207b7SJohn Levon 2261f5207b7SJohn Levon return_implies_state("misc_register", 0, 0, &request_granted, INT_PTR(0)); 2271f5207b7SJohn Levon return_implies_state("misc_register", -MAX_ERRNO, -1, &request_denied, INT_PTR(0)); 2281f5207b7SJohn Levon add_function_hook("misc_deregister", &match_release, INT_PTR(0)); 2291f5207b7SJohn Levon release_function_indicator("misc_deregister"); 2301f5207b7SJohn Levon 2311f5207b7SJohn Levon add_hook(&match_return, RETURN_HOOK); 2321f5207b7SJohn Levon } 233