1 /*
   2  * Copyright (C) 2012 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 functions like:
  20  *
  21  * int foo(int *x)
  22  * {
  23  *      if (*x == 42) {
  24  *              *x = 0;
  25  *              return 1;
  26  *      }
  27  *      return 0;
  28  * }
  29  *
  30  * If we return 1 that means the value of *x has been set to 0.  If we return
  31  * 0 then we have left *x alone.
  32  *
  33  */
  34 
  35 #include "scope.h"
  36 #include "smatch.h"
  37 #include "smatch_slist.h"
  38 #include "smatch_extra.h"
  39 
  40 static int my_id;
  41 
  42 static struct smatch_state *unmatched_state(struct sm_state *sm)
  43 {
  44         return alloc_estate_empty();
  45 }
  46 
  47 static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state)
  48 {
  49         struct expression *faked;
  50         char *left_name;
  51         int ret = 0;
  52         int len;
  53 
  54         if (!__in_fake_assign)
  55                 return 0;
  56         if (!is_whole_rl(estate_rl(state)))
  57                 return 0;
  58         if (get_state(my_id, name, sym))
  59                 return 0;
  60 
  61         faked = get_faked_expression();
  62         if (!faked)
  63                 return 0;
  64         if ((faked->type == EXPR_PREOP || faked->type == EXPR_POSTOP) &&
  65             (faked->op == SPECIAL_INCREMENT || faked->op == SPECIAL_DECREMENT)) {
  66                 faked = strip_expr(faked->unop);
  67                 if (faked->type == EXPR_SYMBOL)
  68                         return 1;
  69                 return 0;
  70         }
  71         if (faked->type != EXPR_ASSIGNMENT)
  72                 return 0;
  73 
  74         left_name = expr_to_var(faked->left);
  75         if (!left_name)
  76                 return 0;
  77 
  78         len = strlen(left_name);
  79         if (strncmp(name, left_name, len) == 0 && name[len] == '-')
  80                 ret = 1;
  81         free_string(left_name);
  82 
  83         return ret;
  84 }
  85 
  86 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
  87 {
  88         if (parent_is_set(name, sym, state))
  89                 return;
  90         if (get_param_num_from_sym(sym) < 0)
  91                 return;
  92         set_state(my_id, name, sym, state);
  93 }
  94 
  95 /*
  96  * This function is is a dirty hack because extra_mod_hook is giving us a NULL
  97  *  sym instead of a vsl.
  98  */
  99 static void match_array_assignment(struct expression *expr)
 100 {
 101         struct expression *array, *offset;
 102         char *name;
 103         struct symbol *sym;
 104         struct range_list *rl;
 105         sval_t sval;
 106         char buf[256];
 107 
 108         if (__in_fake_assign)
 109                 return;
 110 
 111         if (!is_array(expr->left))
 112                 return;
 113         array = get_array_base(expr->left);
 114         offset = get_array_offset(expr->left);
 115 
 116         /* These are handled by extra_mod_hook() */
 117         if (get_value(offset, &sval))
 118                 return;
 119         name = expr_to_var_sym(array, &sym);
 120         if (!name || !sym)
 121                 goto free;
 122         if (get_param_num_from_sym(sym) < 0)
 123                 goto free;
 124         get_absolute_rl(expr->right, &rl);
 125         rl = cast_rl(get_type(expr->left), rl);
 126 
 127         snprintf(buf, sizeof(buf), "*%s", name);
 128         set_state(my_id, buf, sym, alloc_estate_rl(rl));
 129 free:
 130         free_string(name);
 131 }
 132 
 133 /*
 134  * This relies on the fact that these states are stored so that
 135  * foo->bar is before foo->bar->baz.
 136  */
 137 static int parent_set(struct string_list *list, const char *name)
 138 {
 139         char *tmp;
 140         int len;
 141         int ret;
 142 
 143         FOR_EACH_PTR(list, tmp) {
 144                 len = strlen(tmp);
 145                 ret = strncmp(tmp, name, len);
 146                 if (ret < 0)
 147                         continue;
 148                 if (ret > 0)
 149                         return 0;
 150                 if (name[len] == '-')
 151                         return 1;
 152         } END_FOR_EACH_PTR(tmp);
 153 
 154         return 0;
 155 }
 156 
 157 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
 158 {
 159         struct sm_state *sm;
 160         struct smatch_state *extra;
 161         int param;
 162         struct range_list *rl;
 163         const char *param_name;
 164         struct string_list *set_list = NULL;
 165         char *math_str;
 166         char buf[256];
 167         sval_t sval;
 168 
 169         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 170                 if (!estate_rl(sm->state))
 171                         continue;
 172                 extra = get_state(SMATCH_EXTRA, sm->name, sm->sym);
 173                 if (extra) {
 174                         rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
 175                         if (!rl)
 176                                 continue;
 177                 } else {
 178                         rl = estate_rl(sm->state);
 179                 }
 180 
 181                 param = get_param_num_from_sym(sm->sym);
 182                 if (param < 0)
 183                         continue;
 184                 param_name = get_param_name(sm);
 185                 if (!param_name)
 186                         continue;
 187                 if (strcmp(param_name, "$") == 0) {
 188                         insert_string(&set_list, (char *)sm->name);
 189                         continue;
 190                 }
 191 
 192                 if (rl_to_sval(rl, &sval)) {
 193                         insert_string(&set_list, (char *)sm->name);
 194                         sql_insert_return_states(return_id, return_ranges,
 195                                         param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
 196                                         param, param_name, show_rl(rl));
 197                         continue;
 198                 }
 199 
 200                 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
 201                 if (math_str) {
 202                         snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
 203                         insert_string(&set_list, (char *)sm->name);
 204                         sql_insert_return_states(return_id, return_ranges,
 205                                         param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
 206                                         param, param_name, buf);
 207                         continue;
 208                 }
 209 
 210                 /* no useful information here. */
 211                 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
 212                         continue;
 213                 insert_string(&set_list, (char *)sm->name);
 214 
 215                 sql_insert_return_states(return_id, return_ranges,
 216                                          param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
 217                                          param, param_name, show_rl(rl));
 218 
 219         } END_FOR_EACH_SM(sm);
 220 
 221         free_ptr_list((struct ptr_list **)&set_list);
 222 }
 223 
 224 int param_was_set_var_sym(const char *name, struct symbol *sym)
 225 {
 226         struct sm_state *sm;
 227         int len;
 228 
 229         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 230                 if (sm->sym != sym)
 231                         continue;
 232                 len = strlen(sm->name);
 233                 if (strncmp(sm->name, name, len) != 0)
 234                         continue;
 235                 if (name[len] == '\0' ||
 236                     name[len] == '-')
 237                         return 1;
 238         } END_FOR_EACH_SM(sm);
 239 
 240         return 0;
 241 }
 242 
 243 int param_was_set(struct expression *expr)
 244 {
 245         char *name;
 246         struct symbol *sym;
 247         int ret = 0;
 248 
 249         name = expr_to_var_sym(expr, &sym);
 250         if (!name || !sym)
 251                 goto free;
 252 
 253         ret = param_was_set_var_sym(name, sym);
 254 free:
 255         free_string(name);
 256         return ret;
 257 }
 258 
 259 void register_param_set(int id)
 260 {
 261         my_id = id;
 262 
 263         add_extra_mod_hook(&extra_mod_hook);
 264         add_hook(match_array_assignment, ASSIGNMENT_HOOK);
 265         add_unmatched_state_hook(my_id, &unmatched_state);
 266         add_merge_hook(my_id, &merge_estates);
 267         add_split_return_callback(&print_return_value_param);
 268 }
 269