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 static char *get_two_dots(const char *name)
 134 {
 135         static char buf[80];
 136         int i, cnt = 0;
 137 
 138         for (i = 0; i < sizeof(buf); i++) {
 139                 if (name[i] == '.') {
 140                         cnt++;
 141                         if (cnt >= 2) {
 142                                 buf[i] = '\0';
 143                                 return buf;
 144                         }
 145                 }
 146                 buf[i] = name[i];
 147         }
 148         return NULL;
 149 }
 150 
 151 /*
 152  * This relies on the fact that these states are stored so that
 153  * foo->bar is before foo->bar->baz.
 154  */
 155 static int parent_set(struct string_list *list, const char *name)
 156 {
 157         char *tmp;
 158         int len;
 159         int ret;
 160 
 161         FOR_EACH_PTR(list, tmp) {
 162                 len = strlen(tmp);
 163                 ret = strncmp(tmp, name, len);
 164                 if (ret < 0)
 165                         continue;
 166                 if (ret > 0)
 167                         return 0;
 168                 if (name[len] == '-')
 169                         return 1;
 170         } END_FOR_EACH_PTR(tmp);
 171 
 172         return 0;
 173 }
 174 
 175 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit)
 176 {
 177         struct sm_state *sm;
 178         struct smatch_state *extra;
 179         int param;
 180         struct range_list *rl;
 181         const char *param_name;
 182         struct string_list *set_list = NULL;
 183         char *math_str;
 184         char buf[256];
 185         char two_dot[80] = "";
 186         int count = 0;
 187 
 188         FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
 189                 if (!estate_rl(sm->state))
 190                         continue;
 191                 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
 192                 if (extra) {
 193                         rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
 194                         if (!rl)
 195                                 continue;
 196                 } else {
 197                         rl = estate_rl(sm->state);
 198                 }
 199 
 200                 param = get_param_num_from_sym(sm->sym);
 201                 if (param < 0)
 202                         continue;
 203                 param_name = get_param_name(sm);
 204                 if (!param_name)
 205                         continue;
 206                 if (strcmp(param_name, "$") == 0) {
 207                         insert_string(&set_list, (char *)sm->name);
 208                         continue;
 209                 }
 210                 if (is_recursive_member(param_name)) {
 211                         insert_string(&set_list, (char *)sm->name);
 212                         continue;
 213                 }
 214 
 215                 if (is_ignored_kernel_data(param_name)) {
 216                         insert_string(&set_list, (char *)sm->name);
 217                         continue;
 218                 }
 219                 if (limit) {
 220                         char *new = get_two_dots(param_name);
 221 
 222                         if (new) {
 223                                 if (strcmp(new, two_dot) == 0)
 224                                         continue;
 225                                 strncpy(two_dot, new, sizeof(two_dot));
 226                                 sql_insert_return_states(return_id, return_ranges,
 227                                          PARAM_SET, param, new, "s64min-s64max");
 228                                 continue;
 229                         }
 230                 }
 231 
 232                 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
 233                 if (math_str) {
 234                         snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
 235                         insert_string(&set_list, (char *)sm->name);
 236                         sql_insert_return_states(return_id, return_ranges,
 237                                         param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
 238                                         param, param_name, buf);
 239                         continue;
 240                 }
 241 
 242                 /* no useful information here. */
 243                 if (is_whole_rl(rl) && parent_set(set_list, sm->name))
 244                         continue;
 245                 insert_string(&set_list, (char *)sm->name);
 246 
 247                 sql_insert_return_states(return_id, return_ranges,
 248                                          param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
 249                                          param, param_name, show_rl(rl));
 250                 if (limit && ++count > limit)
 251                         break;
 252 
 253         } END_FOR_EACH_SM(sm);
 254 
 255         free_ptr_list((struct ptr_list **)&set_list);
 256 }
 257 
 258 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
 259 {
 260         print_return_value_param_helper(return_id, return_ranges, expr, 0);
 261 }
 262 
 263 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr)
 264 {
 265         print_return_value_param_helper(return_id, return_ranges, expr, 1000);
 266 }
 267 
 268 static int possibly_empty(struct sm_state *sm)
 269 {
 270         struct sm_state *tmp;
 271 
 272         FOR_EACH_PTR(sm->possible, tmp) {
 273                 if (strcmp(tmp->name, "") == 0)
 274                         return 1;
 275         } END_FOR_EACH_PTR(tmp);
 276         return 0;
 277 }
 278 
 279 int param_was_set_var_sym(const char *name, struct symbol *sym)
 280 {
 281         struct sm_state *sm;
 282         char buf[80];
 283         int len, i;
 284 
 285         if (!name)
 286                 return 0;
 287 
 288         len = strlen(name);
 289         if (len >= sizeof(buf))
 290                 len = sizeof(buf) - 1;
 291 
 292         for (i = 0; i <= len; i++) {
 293                 if (name[i] != '-' && name[i] != '\0')
 294                         continue;
 295 
 296                 memcpy(buf, name, i);
 297                 buf[i] = '\0';
 298 
 299                 sm = get_sm_state(my_id, buf, sym);
 300                 if (!sm)
 301                         continue;
 302                 if (possibly_empty(sm))
 303                         continue;
 304                 return 1;
 305         }
 306 
 307         if (name[0] == '*')
 308                 return param_was_set_var_sym(name + 1, sym);
 309 
 310         return 0;
 311 }
 312 
 313 int param_was_set(struct expression *expr)
 314 {
 315         char *name;
 316         struct symbol *sym;
 317         int ret = 0;
 318 
 319         name = expr_to_var_sym(expr, &sym);
 320         if (!name || !sym)
 321                 goto free;
 322 
 323         ret = param_was_set_var_sym(name, sym);
 324 free:
 325         free_string(name);
 326         return ret;
 327 }
 328 
 329 void register_param_set(int id)
 330 {
 331         my_id = id;
 332 
 333         set_dynamic_states(my_id);
 334         add_extra_mod_hook(&extra_mod_hook);
 335         add_hook(match_array_assignment, ASSIGNMENT_HOOK);
 336         add_unmatched_state_hook(my_id, &unmatched_state);
 337         add_merge_hook(my_id, &merge_estates);
 338         add_split_return_callback(&print_return_value_param);
 339 }
 340