1 /*
   2  * Copyright (C) 2017 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  * Say you have assign a function to a function pointer and you assign a
  20  * pointer to the data argument then we want to record some information about
  21  * the argument.  Right now what I mainly want to record is the type of it, I
  22  * guess.
  23  *
  24  */
  25 
  26 #include "smatch.h"
  27 #include "smatch_extra.h"
  28 #include "smatch_slist.h"
  29 #include <ctype.h>
  30 
  31 static int my_id;
  32 
  33 static int assigns_parameters(struct expression *fn, struct expression *arg)
  34 {
  35         int fn_param, arg_param;
  36         char buf[32];
  37 
  38         fn_param = get_param_num(fn);
  39         if (fn_param < 0)
  40                 return 0;
  41 
  42         arg_param = get_param_num(arg);
  43         if (arg_param < 0)
  44                 return 0;
  45 
  46         snprintf(buf, sizeof(buf), "%d", arg_param);
  47         sql_insert_return_implies(FN_ARG_LINK, fn_param, "$", buf);
  48         return 1;
  49 }
  50 
  51 static void link_function_arg(struct expression *fn, int param, struct expression *arg)
  52 {
  53         struct symbol *type;
  54 
  55         if (!fn || !arg)
  56                 return;
  57         if (assigns_parameters(fn, arg))
  58                 return;
  59 
  60         type = get_type(arg);
  61         if (!type || type->type != SYM_PTR)
  62                 return;
  63         type = get_real_base_type(type);
  64         if (!type)
  65                 return;
  66         // FIXME: param shouldn't always be 0?
  67         sql_insert_fn_data_link(fn, PASSES_TYPE, param, "$", type_to_str(type));
  68 }
  69 
  70 char *next_param_name;
  71 struct symbol *next_param_sym;
  72 struct expression *next_fn;
  73 static void match_assign_param(struct expression *expr)
  74 {
  75         struct symbol *sym;
  76         char *name;
  77 
  78         if (!next_param_name)
  79                 return;
  80 
  81         name = expr_to_var_sym(expr->left, &sym);
  82         if (!name || !sym) {
  83                 free_string(name);
  84                 return;
  85         }
  86 
  87         if (sym != next_param_sym ||
  88             strcmp(name, next_param_name) != 0)
  89                 return;
  90 
  91         link_function_arg(next_fn, 0, strip_expr(expr->right));
  92 
  93         next_param_name = 0;
  94         next_param_sym = NULL;
  95         next_fn = NULL;
  96 }
  97 
  98 static int get_arg_ptr(void *_arg_ptr, int argc, char **argv, char **azColName)
  99 {
 100         char **arg_ptr = _arg_ptr;
 101 
 102         *arg_ptr = NULL;
 103         if (argc != 1)
 104                 return 0;
 105         *arg_ptr = alloc_string(argv[0]);
 106         return 0;
 107 }
 108 
 109 static char *get_data_member(char *fn_member, struct expression *expr, struct symbol **sym)
 110 {
 111         struct symbol *tmp_sym;
 112         char *fn_str;
 113         char *arg_ptr = NULL;
 114         char *end_type;
 115         int len_ptr, len_str;
 116         char buf[128];
 117 
 118         *sym = NULL;
 119         run_sql(get_arg_ptr, &arg_ptr,
 120                 "select data from fn_ptr_data_link where fn_ptr = '%s';", fn_member);
 121         if (!arg_ptr)
 122                 return NULL;
 123         end_type = strchr(arg_ptr, '>');
 124         if (!end_type)
 125                 return NULL;
 126         end_type++;
 127         fn_str = expr_to_var_sym(expr, &tmp_sym);
 128         if (!fn_str || !tmp_sym)
 129                 return NULL;
 130         len_ptr = strlen(fn_member);
 131         len_str = strlen(fn_str);
 132         while (len_str > 0 && len_ptr > 0) {
 133                 if (fn_str[len_str - 1] != fn_member[len_ptr - 1])
 134                         break;
 135                 if (fn_str[len_str - 1] == '>')
 136                         break;
 137                 len_str--;
 138                 len_ptr--;
 139         }
 140 
 141         strncpy(buf, fn_str, sizeof(buf));
 142         snprintf(buf + len_str, sizeof(buf) - len_str, end_type);
 143         *sym = tmp_sym;
 144         return alloc_string(buf);
 145 }
 146 
 147 static void match_assign_function(struct expression *expr)
 148 {
 149         struct expression *right, *arg;
 150         struct symbol *sym;
 151         char *data_member;
 152         struct symbol *type;
 153         char *member_name;
 154 
 155         right = strip_expr(expr->right);
 156         if (right->type == EXPR_PREOP && right->op == '&')
 157                 right = strip_expr(right->unop);
 158 
 159         type = get_type(right);
 160         if (type && type->type == SYM_PTR)
 161                 type = get_real_base_type(type);
 162         if (!type || type->type != SYM_FN)
 163                 return;
 164 
 165         member_name = get_member_name(expr->left);
 166         if (!member_name)
 167                 return;
 168 
 169         data_member = get_data_member(member_name, expr->left, &sym);
 170         if (!data_member || !sym) {
 171                 free_string(data_member);
 172                 data_member = NULL;
 173         }
 174 
 175         arg = get_assigned_expr_name_sym(data_member, sym);
 176         if (arg) {
 177                 link_function_arg(right, 0, arg);
 178         } else {
 179                 next_param_name = data_member;
 180                 next_param_sym = sym;
 181                 next_fn = right;
 182         }
 183 }
 184 
 185 static int is_recursive_call(struct expression *call)
 186 {
 187         if (call->fn->type != EXPR_SYMBOL)
 188                 return 0;
 189         if (call->fn->symbol == cur_func_sym)
 190                 return 1;
 191         return 0;
 192 }
 193 
 194 static void check_passes_fn_and_data(struct expression *call, struct expression *fn, char *key, char *value)
 195 {
 196         struct expression *arg;
 197         struct symbol *type;
 198         int data_nr;
 199 
 200         if (is_recursive_call(call))
 201                 return;
 202 
 203         type = get_type(fn);
 204         if (!type || type->type != SYM_FN)
 205                 return;
 206 
 207         if (!isdigit(value[0]))
 208                 return;
 209         data_nr = atoi(value);
 210         arg = get_argument_from_call_expr(call->args, data_nr);
 211         if (!arg)
 212                 return;
 213         link_function_arg(fn, 0, arg);
 214 }
 215 
 216 static void match_end_func(struct symbol *sym)
 217 {
 218         next_param_sym = NULL;
 219         next_fn = NULL;
 220 }
 221 
 222 void register_about_fn_ptr_arg(int id)
 223 {
 224         my_id = id;
 225 
 226         if (0 && !option_info)
 227                 return;
 228         add_hook(match_assign_param, ASSIGNMENT_HOOK);
 229         add_hook(match_assign_function, ASSIGNMENT_HOOK);
 230         select_return_implies_hook(FN_ARG_LINK, &check_passes_fn_and_data);
 231         add_hook(&match_end_func, END_FUNC_HOOK);
 232 }