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