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  * This is for smatch_extra.c to use.  It sort of like check_assigned_expr.c but
  20  * more limited.  Say a function returns "64min-s64max[$0->data]" and the caller
  21  * does "struct whatever *p = get_data(dev);" then we want to record that p is
  22  * now the same as "dev->data".  Then if we update "p->foo" it means we can
  23  * update "dev->data->foo" as well.
  24  *
  25  */
  26 
  27 #include "smatch.h"
  28 #include "smatch_slist.h"
  29 #include "smatch_extra.h"
  30 
  31 static int my_id;
  32 static int link_id;
  33 
  34 static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym)
  35 {
  36         struct smatch_state *state;
  37 
  38         state = __alloc_smatch_state(0);
  39         state->name = alloc_sname(name);
  40         state->data = sym;
  41         return state;
  42 }
  43 
  44 static void undef(struct sm_state *sm, struct expression *mod_expr)
  45 {
  46         if (__in_fake_parameter_assign)
  47                 return;
  48         set_state(my_id, sm->name, sm->sym, &undefined);
  49 }
  50 
  51 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
  52 {
  53         struct smatch_state *state;
  54         int skip;
  55         char buf[256];
  56 
  57         /* skip 'foo->'.  This was checked in the caller. */
  58         skip = sym->ident->len + 2;
  59 
  60         state = get_state(my_id, sym->ident->name, sym);
  61         if (!state || !state->data)
  62                 return NULL;
  63 
  64         snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip);
  65         *new_sym = state->data;
  66         return alloc_string(buf);
  67 }
  68 
  69 static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
  70 {
  71         int len;
  72         char buf[256];
  73 
  74         if (sm->state->data != sym)
  75                 return NULL;
  76         len = strlen(sm->state->name);
  77         if (strncmp(name, sm->state->name, len) != 0)
  78                 return NULL;
  79 
  80         if (name[len] == '.')
  81                 return NULL;
  82         if (!stack && name[len] != '-')
  83                 return NULL;
  84         snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
  85         *new_sym = sm->sym;
  86         return alloc_string(buf);
  87 }
  88 
  89 /*
  90  * Normally, we expect people to consistently refer to variables by the shortest
  91  * name.  So they use "b->a" instead of "foo->bar.a" when both point to the
  92  * same memory location.  However, when we're dealing across function boundaries
  93  * then sometimes we pass frob(foo) which sets foo->bar.a.  In that case, we
  94  * translate it to the shorter name.  Smatch extra updates the shorter name,
  95  * which in turn updates the longer name.
  96  *
  97  */
  98 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
  99 {
 100         char *ret;
 101         struct sm_state *sm;
 102 
 103         *new_sym = NULL;
 104 
 105         FOR_EACH_SM(__get_cur_stree(), sm) {
 106                 if (sm->owner == my_id) {
 107                         ret = map_my_state_long_to_short(sm, name, sym, new_sym, use_stack);
 108                         if (ret) {
 109                                 if (local_debug)
 110                                         sm_msg("%s: my_state: name = '%s' sm = '%s'",
 111                                                __func__, name, show_sm(sm));
 112                                 return ret;
 113                         }
 114                         continue;
 115                 }
 116         } END_FOR_EACH_SM(sm);
 117 
 118         return NULL;
 119 }
 120 
 121 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
 122 {
 123         char *name;
 124         struct symbol *start_sym;
 125         struct smatch_state *state;
 126 
 127         *sym = NULL;
 128 
 129         name = expr_to_str_sym(expr, &start_sym);
 130         if (!name)
 131                 return NULL;
 132         if (expr->type == EXPR_CALL)
 133                 start_sym = expr_to_sym(expr->fn);
 134 
 135         state = get_state(my_id, name, start_sym);
 136         free_string(name);
 137         if (!state || !state->data)
 138                 return NULL;
 139 
 140         *sym = state->data;
 141         return alloc_string(state->name);
 142 }
 143 
 144 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string)
 145 {
 146         const char *p = return_string;
 147         char *close;
 148         int param;
 149         struct expression *arg, *new;
 150         char *right_name;
 151         struct symbol *right_sym;
 152         char buf[256];
 153 
 154         while (*p && *p != '[')
 155                 p++;
 156         if (!*p)
 157                 return;
 158         p++;
 159         if (*p != '$')
 160                 return;
 161 
 162         snprintf(buf, sizeof(buf), "%s", p);
 163         close = strchr(buf, ']');
 164         if (!close)
 165                 return;
 166         *close = '\0';
 167 
 168         param = atoi(buf + 1);
 169         arg = get_argument_from_call_expr(call->args, param);
 170         if (!arg)
 171                 return;
 172 
 173         new = gen_expression_from_key(arg, buf);
 174         if (!new)
 175                 return;
 176 
 177         right_name = expr_to_var_sym(new, &right_sym);
 178         if (!right_name || !right_sym)
 179                 goto free;
 180 
 181         set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym));
 182         store_link(link_id, right_name, right_sym, left_name, left_sym);
 183 
 184 free:
 185         free_string(right_name);
 186 }
 187 
 188 void __add_return_to_param_mapping(struct expression *expr, const char *return_string)
 189 {
 190         struct expression *call;
 191         char *left_name = NULL;
 192         struct symbol *left_sym;
 193 
 194         if (expr->type == EXPR_ASSIGNMENT) {
 195                 left_name = expr_to_var_sym(expr->left, &left_sym);
 196                 if (!left_name || !left_sym)
 197                         goto free;
 198 
 199                 call = strip_expr(expr->right);
 200                 if (call->type != EXPR_CALL)
 201                         goto free;
 202 
 203                 store_mapping_helper(left_name, left_sym, call, return_string);
 204                 goto free;
 205         }
 206 
 207         if (expr->type == EXPR_CALL &&
 208             expr_get_parent_stmt(expr) &&
 209             expr_get_parent_stmt(expr)->type == STMT_RETURN) {
 210                 call = strip_expr(expr);
 211                 left_sym = expr_to_sym(call->fn);
 212                 if (!left_sym)
 213                         return;
 214                 left_name = expr_to_str(call);
 215                 if (!left_name)
 216                         return;
 217 
 218                 store_mapping_helper(left_name, left_sym, call, return_string);
 219                 goto free;
 220 
 221         }
 222 
 223 free:
 224         free_string(left_name);
 225 }
 226 
 227 void register_return_to_param(int id)
 228 {
 229         my_id = id;
 230         set_dynamic_states(my_id);
 231         add_modification_hook(my_id, &undef);
 232 }
 233 
 234 void register_return_to_param_links(int id)
 235 {
 236         link_id = id;
 237         set_up_link_functions(my_id, link_id);
 238 }
 239