1 /* 2 * Copyright (C) 2009 Dan Carpenter. 3 * 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. 8 * 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. 13 * 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 16 */ 17 18 /* 19 * This check is supposed to find bugs in reference counting using dev_hold() 20 * and dev_put(). 21 * 22 * When a device is first held, if an error happens later in the function 23 * it needs to be released on all the error paths. 24 * 25 */ 26 27 #include "smatch.h" 28 #include "smatch_extra.h" 29 #include "smatch_slist.h" 30 31 static int my_id; 32 33 STATE(held); 34 STATE(released); 35 36 static void match_dev_hold(const char *fn, struct expression *expr, void *data) 37 { 38 struct expression *arg_expr; 39 40 arg_expr = get_argument_from_call_expr(expr->args, 0); 41 set_state_expr(my_id, arg_expr, &held); 42 } 43 44 static void match_dev_put(const char *fn, struct expression *expr, void *data) 45 { 46 struct expression *arg_expr; 47 48 arg_expr = get_argument_from_call_expr(expr->args, 0); 49 set_state_expr(my_id, arg_expr, &released); 50 } 51 52 static void match_returns_held(const char *fn, struct expression *call_expr, 53 struct expression *assign_expr, void *unused) 54 { 55 if (assign_expr) 56 set_state_expr(my_id, assign_expr->left, &held); 57 } 58 59 static void match_returns_null(const char *fn, struct expression *call_expr, 60 struct expression *assign_expr, void *unused) 61 { 62 if (assign_expr) 63 set_state_expr(my_id, assign_expr->left, &released); 64 } 65 66 static void check_for_held(void) 67 { 68 struct stree *stree; 69 struct sm_state *tmp; 70 71 stree = __get_cur_stree(); 72 FOR_EACH_MY_SM(my_id, stree, tmp) { 73 if (slist_has_state(tmp->possible, &held)) { 74 sm_warning("'%s' held on error path.", 75 tmp->name); 76 } 77 } END_FOR_EACH_SM(tmp); 78 } 79 80 static void print_returns_held(struct expression *expr) 81 { 82 struct sm_state *sm; 83 84 if (!option_info) 85 return; 86 sm = get_sm_state_expr(my_id, expr); 87 if (!sm) 88 return; 89 if (slist_has_state(sm->possible, &held)) 90 sm_info("returned dev is held."); 91 } 92 93 static void match_return(struct expression *ret_value) 94 { 95 print_returns_held(ret_value); 96 if (!is_error_return(ret_value)) 97 return; 98 check_for_held(); 99 } 100 101 static void register_returns_held_funcs(void) 102 { 103 struct token *token; 104 const char *func; 105 106 token = get_tokens_file("kernel.returns_held_funcs"); 107 if (!token) 108 return; 109 if (token_type(token) != TOKEN_STREAMBEGIN) 110 return; 111 token = token->next; 112 while (token_type(token) != TOKEN_STREAMEND) { 113 if (token_type(token) != TOKEN_IDENT) 114 return; 115 func = show_ident(token->ident); 116 return_implies_state(func, valid_ptr_min, valid_ptr_max, 117 &match_returns_held, NULL); 118 return_implies_state(func, 0, 0, &match_returns_null, 119 NULL); 120 token = token->next; 121 } 122 clear_token_alloc(); 123 } 124 125 void check_held_dev(int id) 126 { 127 if (option_project != PROJ_KERNEL) 128 return; 129 130 my_id = id; 131 add_function_hook("dev_hold", &match_dev_hold, NULL); 132 add_function_hook("dev_put", &match_dev_put, NULL); 133 register_returns_held_funcs(); 134 add_hook(&match_return, RETURN_HOOK); 135 }