1 /* 2 * Copyright (C) 2016 Oracle. 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 * What we're trying to do here is record links between function pointers and 20 * function data. If you have foo->function(foo->data); that's very easy. But 21 * the problem is maybe when you pass the function and the data as parameters. 22 * 23 */ 24 25 #include "smatch.h" 26 #include <ctype.h> 27 28 static int my_id; 29 30 static void save_in_fn_ptr_data_link_table(struct expression *fn, struct expression *arg) 31 { 32 struct symbol *fn_sym, *arg_sym; 33 struct symbol *type; 34 char *fn_name, *arg_name; 35 int sym_len; 36 char fn_buf[128]; 37 char arg_buf[128]; 38 39 fn_name = expr_to_var_sym(fn, &fn_sym); 40 arg_name = expr_to_var_sym(arg, &arg_sym); 41 if (!fn_sym || !fn_sym->ident || !arg_sym || !fn_name || !arg_name) 42 goto free; 43 if (fn_sym != arg_sym) 44 goto free; 45 46 sym_len = fn_sym->ident->len; 47 48 /* This is ignoring 49 * net/mac80211/driver-ops.h:482 drv_sta_remove() FN: local->ops->sta_remove ARG: &local->hw 50 * but ideally the restriction can be removed later. 51 */ 52 if (strncmp(fn_name, arg_name, sym_len) != 0) 53 goto free; 54 55 type = get_real_base_type(fn_sym); 56 if (!type) 57 goto free; 58 if (type->type != SYM_PTR) 59 goto free; 60 type = get_real_base_type(type); 61 if (!type || type->type != SYM_STRUCT || !type->ident) 62 goto free; 63 64 snprintf(fn_buf, sizeof(fn_buf), "(struct %s)%s", type->ident->name, 65 fn_name + sym_len); 66 67 snprintf(arg_buf, sizeof(arg_buf), "(struct %s)%s", type->ident->name, 68 arg_name + sym_len); 69 70 sql_insert_fn_ptr_data_link(fn_buf, arg_buf); 71 free: 72 free_string(arg_name); 73 free_string(fn_name); 74 } 75 76 static int print_calls_parameter(struct expression *call) 77 { 78 struct expression *arg; 79 int fn_param, arg_param; 80 char buf[32]; 81 82 fn_param = get_param_num(call->fn); 83 if (fn_param < 0) 84 return 0; 85 86 arg = get_argument_from_call_expr(call->args, 0); 87 if (!arg) 88 return 0; 89 90 arg_param = get_param_num(arg); 91 if (arg_param < 0) 92 return 0; 93 94 snprintf(buf, sizeof(buf), "%d", arg_param); 95 sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf); 96 return 0; 97 } 98 99 static int print_call_is_linked(struct expression *call) 100 { 101 struct expression *fn, *tmp; 102 struct expression *arg; 103 struct symbol *fn_sym; 104 struct symbol *arg_sym = NULL; 105 int i; 106 107 fn = strip_expr(call->fn); 108 tmp = get_assigned_expr(fn); 109 if (tmp) 110 fn = tmp; 111 if (fn->type != EXPR_DEREF || !fn->member) 112 return 0; 113 114 fn_sym = expr_to_sym(fn); 115 if (!fn_sym) 116 return 0; 117 118 i = -1; 119 FOR_EACH_PTR(call->args, arg) { 120 i++; 121 tmp = get_assigned_expr(arg); 122 if (tmp) 123 arg = tmp; 124 arg_sym = expr_to_sym(arg); 125 if (arg_sym == fn_sym) { 126 save_in_fn_ptr_data_link_table(fn, arg); 127 return 1; 128 } 129 } END_FOR_EACH_PTR(arg); 130 131 return 0; 132 } 133 134 static int is_recursive_call(struct expression *call) 135 { 136 if (call->fn->type != EXPR_SYMBOL) 137 return 0; 138 if (call->fn->symbol == cur_func_sym) 139 return 1; 140 return 0; 141 } 142 143 static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value) 144 { 145 struct expression *arg; 146 struct expression *tmp; 147 struct symbol *fn_sym, *arg_sym; 148 struct symbol *type; 149 int data_nr; 150 int fn_param, arg_param; 151 152 if (is_recursive_call(call)) 153 return; 154 155 type = get_type(fn); 156 if (!type || type->type != SYM_PTR) 157 return; 158 type = get_real_base_type(type); 159 if (!type || type->type != SYM_FN) 160 return; 161 tmp = get_assigned_expr(fn); 162 if (tmp) 163 fn = tmp; 164 165 if (!isdigit(value[0])) 166 return; 167 data_nr = atoi(value); 168 arg = get_argument_from_call_expr(call->args, data_nr); 169 if (!arg) 170 return; 171 tmp = get_assigned_expr(arg); 172 if (tmp) 173 arg = tmp; 174 175 fn_param = get_param_num(fn); 176 arg_param = get_param_num(arg); 177 if (fn_param >= 0 && arg_param >= 0) { 178 char buf[32]; 179 180 snprintf(buf, sizeof(buf), "%d", arg_param); 181 sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf); 182 return; 183 } 184 185 fn_sym = expr_to_sym(fn); 186 if (!fn_sym) 187 return; 188 arg_sym = expr_to_sym(arg); 189 if (arg_sym != fn_sym) 190 return; 191 save_in_fn_ptr_data_link_table(fn, tmp); 192 } 193 194 static void match_call_info(struct expression *call) 195 { 196 if (print_calls_parameter(call)) 197 return; 198 if (print_call_is_linked(call)) 199 return; 200 } 201 202 void register_fn_arg_link(int id) 203 { 204 my_id = id; 205 206 if (!option_info) 207 return; 208 209 add_hook(&match_call_info, FUNCTION_CALL_HOOK); 210 select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data); 211 } 212 213