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 doing here is saving all the possible values for static variables.
  20  * Later on we might do globals as well.
  21  *
  22  */
  23 
  24 #include "smatch.h"
  25 #include "smatch_slist.h"
  26 #include "smatch_extra.h"
  27 
  28 static int my_id;
  29 static struct stree *vals;
  30 
  31 static int save_rl(void *_rl, int argc, char **argv, char **azColName)
  32 {
  33         unsigned long *rl = _rl;
  34 
  35         *rl = strtoul(argv[0], NULL, 10);
  36         return 0;
  37 }
  38 
  39 static struct range_list *select_orig(mtag_t tag, int offset)
  40 {
  41         struct range_list *rl = NULL;
  42 
  43         mem_sql(&save_rl, &rl, "select value from mtag_data where tag = %lld and offset = %d;",
  44                 tag, offset);
  45         return rl;
  46 }
  47 
  48 static int is_kernel_param(const char *name)
  49 {
  50         struct sm_state *tmp;
  51         char buf[256];
  52 
  53         /*
  54          * I'm ignoring these because otherwise Smatch thinks that kernel
  55          * parameters are always set to the default.
  56          *
  57          */
  58 
  59         if (option_project != PROJ_KERNEL)
  60                 return 0;
  61 
  62         snprintf(buf, sizeof(buf), "__param_%s.arg", name);
  63 
  64         FOR_EACH_SM(vals, tmp) {
  65                 if (strcmp(tmp->name, buf) == 0)
  66                         return 1;
  67         } END_FOR_EACH_SM(tmp);
  68 
  69         return 0;
  70 }
  71 
  72 static bool is_ignored_macro(struct expression *expr)
  73 {
  74         char *macro;
  75 
  76         macro = get_macro_name(expr->pos);
  77         if (!macro)
  78                 return false;
  79         if (strcmp(macro, "EXPORT_SYMBOL") == 0)
  80                 return true;
  81         return false;
  82 }
  83 
  84 static bool is_head_next(struct expression *expr)
  85 {
  86         struct symbol *type;
  87 
  88         /* Smatch thinks head->next == head is always true.  *sad face* */
  89 
  90         if (option_project != PROJ_KERNEL)
  91                 return false;
  92 
  93         if (expr->type != EXPR_DEREF)
  94                 return false;
  95         if (!expr->member || !expr->member->name ||
  96             strcmp(expr->member->name, "next") != 0)
  97                 return false;
  98 
  99         type = get_type(expr->deref);
 100         if (!type)
 101                 return false;
 102         if (type->type == SYM_PTR)
 103                 type = get_real_base_type(type);
 104         if (type->type != SYM_STRUCT)
 105                 return false;
 106         if (!type->ident || !type->ident->name ||
 107             strcmp(type->ident->name, "list_head") != 0)
 108                 return false;
 109         return true;
 110 }
 111 
 112 mtag_t ignored_mtag;
 113 static bool is_ignored_tag(mtag_t tag)
 114 {
 115         if (tag == ignored_mtag)
 116                 return true;
 117         return false;
 118 }
 119 
 120 static void insert_mtag_data(mtag_t tag, int offset, struct range_list *rl)
 121 {
 122         if (is_ignored_tag(tag))
 123                 return;
 124 
 125         rl = clone_rl_permanent(rl);
 126 
 127         mem_sql(NULL, NULL, "delete from mtag_data where tag = %lld and offset = %d and type = %d",
 128                 tag, offset, DATA_VALUE);
 129         mem_sql(NULL, NULL, "insert into mtag_data values (%lld, %d, %d, '%lu');",
 130                 tag, offset, DATA_VALUE, (unsigned long)rl);
 131 }
 132 
 133 static bool invalid_type(struct symbol *type)
 134 {
 135         if (!type)
 136                 return true;
 137         if (type == &void_ctype)
 138                 return true;
 139         if (type->type == SYM_STRUCT ||
 140             type->type == SYM_ARRAY ||
 141             type->type == SYM_UNION)
 142                 return true;
 143         return false;
 144 }
 145 
 146 static bool parent_is_fresh_alloc(struct expression *expr)
 147 {
 148         struct symbol *sym;
 149 
 150         sym = expr_to_sym(expr);
 151         if (!sym || !sym->ident)
 152                 return false;
 153         return is_fresh_alloc_var_sym(sym->ident->name, sym);
 154 }
 155 
 156 void update_mtag_data(struct expression *expr, struct smatch_state *state)
 157 {
 158         struct range_list *orig, *new;
 159         struct symbol *type;
 160         char *name;
 161         mtag_t tag;
 162         int offset;
 163 
 164         if (!expr)
 165                 return;
 166         if (is_local_variable(expr))
 167                 return;
 168         if (is_ignored_macro(expr))
 169                 return;
 170         if (is_head_next(expr))
 171                 return;
 172         name = expr_to_var(expr);
 173         if (is_kernel_param(name)) {
 174                 free_string(name);
 175                 return;
 176         }
 177         free_string(name);
 178 
 179         if (!expr_to_mtag_offset(expr, &tag, &offset))
 180                 return;
 181 
 182         type = get_type(expr);
 183         if (offset == 0 && invalid_type(type))
 184                 return;
 185 
 186         if (parent_is_fresh_alloc(expr))
 187                 orig = NULL;
 188         else
 189                 orig = select_orig(tag, offset);
 190         new = rl_union(orig, estate_rl(state));
 191         insert_mtag_data(tag, offset, new);
 192 }
 193 
 194 static void match_global_assign(struct expression *expr)
 195 {
 196         struct range_list *rl;
 197         mtag_t tag;
 198         int offset;
 199         char *name;
 200 
 201         if (is_ignored_macro(expr))
 202                 return;
 203         if (is_head_next(expr->left))
 204                 return;
 205         name = expr_to_var(expr->left);
 206         if (is_kernel_param(name)) {
 207                 free_string(name);
 208                 return;
 209         }
 210         free_string(name);
 211 
 212         if (!expr_to_mtag_offset(expr->left, &tag, &offset))
 213                 return;
 214 
 215         get_absolute_rl(expr->right, &rl);
 216         insert_mtag_data(tag, offset, rl);
 217 }
 218 
 219 static int save_mtag_data(void *_unused, int argc, char **argv, char **azColName)
 220 {
 221         struct range_list *rl;
 222 
 223         if (argc != 4) {
 224                 sm_msg("Error saving mtag data");
 225                 return 0;
 226         }
 227         if (!option_info)
 228                 return 0;
 229 
 230         rl = (struct range_list *)strtoul(argv[3], NULL, 10);
 231         sm_msg("SQL: insert into mtag_data values ('%s', '%s', '%s', '%s');",
 232                argv[0], argv[1], argv[2], show_rl(rl));
 233 
 234         return 0;
 235 }
 236 
 237 static void match_end_file(struct symbol_list *sym_list)
 238 {
 239         mem_sql(&save_mtag_data, NULL, "select * from mtag_data where type = %d;",
 240                 DATA_VALUE);
 241 }
 242 
 243 struct db_info {
 244         struct symbol *type;
 245         struct range_list *rl;
 246 };
 247 
 248 static int get_vals(void *_db_info, int argc, char **argv, char **azColName)
 249 {
 250         struct db_info *db_info = _db_info;
 251         struct range_list *tmp;
 252 
 253         str_to_rl(db_info->type, argv[0], &tmp);
 254         if (db_info->rl)
 255                 db_info->rl = rl_union(db_info->rl, tmp);
 256         else
 257                 db_info->rl = tmp;
 258 
 259         return 0;
 260 }
 261 
 262 struct db_cache_results {
 263         mtag_t tag;
 264         struct range_list *rl;
 265 };
 266 static struct db_cache_results cached_results[8];
 267 
 268 static int get_rl_from_mtag_offset(mtag_t tag, int offset, struct symbol *type, struct range_list **rl)
 269 {
 270         struct db_info db_info = {};
 271         mtag_t merged = tag | offset;
 272         static int idx;
 273         int ret;
 274         int i;
 275 
 276         for (i = 0; i < ARRAY_SIZE(cached_results); i++) {
 277                 if (merged == cached_results[i].tag) {
 278                         if (cached_results[i].rl) {
 279                                 *rl = cached_results[i].rl;
 280                                 return 1;
 281                         }
 282                         return 0;
 283                 }
 284         }
 285 
 286         db_info.type = type;
 287 
 288         run_sql(get_vals, &db_info,
 289                 "select value from mtag_data where tag = %lld and offset = %d and type = %d;",
 290                 tag, offset, DATA_VALUE);
 291         if (!db_info.rl || is_whole_rl(db_info.rl)) {
 292                 db_info.rl = NULL;
 293                 ret = 0;
 294                 goto update_cache;
 295         }
 296 
 297         *rl = db_info.rl;
 298         ret = 1;
 299 
 300 update_cache:
 301         cached_results[idx].tag = merged;
 302         cached_results[idx].rl = db_info.rl;
 303         idx = (idx + 1) % ARRAY_SIZE(cached_results);
 304 
 305         return ret;
 306 }
 307 
 308 static void clear_cache(struct symbol *sym)
 309 {
 310         memset(cached_results, 0, sizeof(cached_results));
 311 }
 312 
 313 int get_mtag_rl(struct expression *expr, struct range_list **rl)
 314 {
 315         struct symbol *type;
 316         mtag_t tag;
 317         int offset;
 318 
 319         if (is_local_variable(expr))
 320                 return 0;
 321         if (!expr_to_mtag_offset(expr, &tag, &offset))
 322                 return 0;
 323         if (offset >= MTAG_OFFSET_MASK)
 324                 return 0;
 325 
 326         type = get_type(expr);
 327         if (invalid_type(type))
 328                 return 0;
 329 
 330         return get_rl_from_mtag_offset(tag, offset, type, rl);
 331 }
 332 
 333 void register_mtag_data(int id)
 334 {
 335         my_id = id;
 336 
 337         ignored_mtag = str_to_mtag("extern boot_params");
 338         add_hook(&clear_cache, FUNC_DEF_HOOK);
 339 
 340 //      if (!option_info)
 341 //              return;
 342         add_hook(&match_global_assign, GLOBAL_ASSIGNMENT_HOOK);
 343         add_hook(&match_end_file, END_FILE_HOOK);
 344 }
 345