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 346