1 /* 2 * Copyright (C) 2009 Dan Carpenter. 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 * There are a number of ways that variables are modified: 20 * 1) assignment 21 * 2) increment/decrement 22 * 3) assembly 23 * 4) inside functions. 24 * 25 * For setting stuff inside a function then, of course, it's more accurate if 26 * you have the cross function database built. Otherwise we are super 27 * aggressive about marking things as modified and if you have "frob(foo);" then 28 * we assume "foo->bar" is modified. 29 */ 30 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include "smatch.h" 34 #include "smatch_extra.h" 35 #include "smatch_slist.h" 36 37 enum { 38 EARLY = 0, 39 LATE = 1, 40 BOTH = 2 41 }; 42 43 static modification_hook **hooks; 44 static modification_hook **hooks_late; 45 46 ALLOCATOR(modification_data, "modification data"); 47 48 static int my_id; 49 static struct smatch_state *alloc_my_state(struct expression *expr, struct smatch_state *prev) 50 { 51 struct smatch_state *state; 52 struct modification_data *data; 53 char *name; 54 55 expr = strip_expr(expr); 56 name = expr_to_str(expr); 57 if (!name) 58 return NULL; 59 60 state = __alloc_smatch_state(0); 61 state->name = alloc_sname(name); 62 free_string(name); 63 64 data = __alloc_modification_data(0); 65 data->prev = prev; 66 data->cur = expr; 67 state->data = data; 68 69 return state; 70 } 71 72 void add_modification_hook(int owner, modification_hook *call_back) 73 { 74 if (hooks[owner]) 75 sm_fatal("multiple modification hooks for %s", check_name(owner)); 76 hooks[owner] = call_back; 77 } 78 79 void add_modification_hook_late(int owner, modification_hook *call_back) 80 { 81 if (hooks_late[owner]) 82 sm_fatal("multiple late modification hooks for %s", check_name(owner)); 83 hooks_late[owner] = call_back; 84 } 85 86 static int matches(char *name, struct symbol *sym, struct sm_state *sm) 87 { 88 int len; 89 90 if (sym != sm->sym) 91 return false; 92 93 len = strlen(name); 94 if (strncmp(sm->name, name, len) == 0) { 95 if (sm->name[len] == '\0') 96 return true; 97 if (sm->name[len] == '-' || sm->name[len] == '.') 98 return true; 99 } 100 if (sm->name[0] != '*') 101 return false; 102 if (strncmp(sm->name + 1, name, len) == 0) { 103 if (sm->name[len + 1] == '\0') 104 return true; 105 if (sm->name[len + 1] == '-' || sm->name[len + 1] == '.') 106 return true; 107 } 108 return false; 109 } 110 111 static void call_modification_hooks_name_sym(char *name, struct symbol *sym, struct expression *mod_expr, int late) 112 { 113 struct sm_state *sm; 114 struct smatch_state *prev; 115 int match; 116 117 prev = get_state(my_id, name, sym); 118 119 if (cur_func_sym && !__in_fake_assign) 120 set_state(my_id, name, sym, alloc_my_state(mod_expr, prev)); 121 122 FOR_EACH_SM(__get_cur_stree(), sm) { 123 if (sm->owner > num_checks) 124 continue; 125 match = matches(name, sym, sm); 126 if (!match) 127 continue; 128 129 if (late == EARLY || late == BOTH) { 130 if (hooks[sm->owner]) 131 (hooks[sm->owner])(sm, mod_expr); 132 } 133 if (late == LATE || late == BOTH) { 134 if (hooks_late[sm->owner]) 135 (hooks_late[sm->owner])(sm, mod_expr); 136 } 137 138 } END_FOR_EACH_SM(sm); 139 } 140 141 static void call_modification_hooks(struct expression *expr, struct expression *mod_expr, int late) 142 { 143 char *name; 144 struct symbol *sym; 145 146 if (late == LATE) 147 update_mtag_data(expr); 148 149 name = expr_to_known_chunk_sym(expr, &sym); 150 if (!name) 151 goto free; 152 call_modification_hooks_name_sym(name, sym, mod_expr, late); 153 free: 154 free_string(name); 155 } 156 157 static void db_param_add(struct expression *expr, int param, char *key, char *value) 158 { 159 struct expression *arg, *gen_expr; 160 char *name, *other_name; 161 struct symbol *sym, *other_sym; 162 163 while (expr->type == EXPR_ASSIGNMENT) 164 expr = strip_expr(expr->right); 165 if (expr->type != EXPR_CALL) 166 return; 167 168 arg = get_argument_from_call_expr(expr->args, param); 169 if (!arg) 170 return; 171 172 gen_expr = gen_expression_from_key(arg, key); 173 if (gen_expr) 174 update_mtag_data(gen_expr); 175 176 name = get_variable_from_key(arg, key, &sym); 177 if (!name || !sym) 178 goto free; 179 180 __in_fake_assign++; 181 call_modification_hooks_name_sym(name, sym, expr, BOTH); 182 __in_fake_assign--; 183 184 other_name = get_other_name_sym(name, sym, &other_sym); 185 if (other_name) { 186 __in_fake_assign++; 187 call_modification_hooks_name_sym(other_name, other_sym, expr, BOTH); 188 __in_fake_assign--; 189 free_string(other_name); 190 } 191 192 free: 193 free_string(name); 194 } 195 196 static void match_assign(struct expression *expr, int late) 197 { 198 call_modification_hooks(expr->left, expr, late); 199 } 200 201 static void unop_expr(struct expression *expr, int late) 202 { 203 if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT) 204 return; 205 206 call_modification_hooks(expr->unop, expr, late); 207 } 208 209 static void match_call(struct expression *expr) 210 { 211 struct expression *arg, *tmp; 212 213 /* If we have the DB then trust the DB */ 214 if (!option_no_db) 215 return; 216 217 FOR_EACH_PTR(expr->args, arg) { 218 tmp = strip_expr(arg); 219 if (tmp->type == EXPR_PREOP && tmp->op == '&') 220 call_modification_hooks(tmp->unop, expr, BOTH); 221 else 222 call_modification_hooks(deref_expression(tmp), expr, BOTH); 223 } END_FOR_EACH_PTR(arg); 224 } 225 226 static void asm_expr(struct statement *stmt, int late) 227 { 228 struct expression *expr; 229 int state = 0; 230 231 FOR_EACH_PTR(stmt->asm_outputs, expr) { 232 switch (state) { 233 case 0: /* identifier */ 234 case 1: /* constraint */ 235 state++; 236 continue; 237 case 2: /* expression */ 238 state = 0; 239 call_modification_hooks(expr, NULL, late); 240 continue; 241 } 242 } END_FOR_EACH_PTR(expr); 243 } 244 245 246 static void match_assign_early(struct expression *expr) 247 { 248 match_assign(expr, EARLY); 249 } 250 251 static void unop_expr_early(struct expression *expr) 252 { 253 unop_expr(expr, EARLY); 254 } 255 256 static void asm_expr_early(struct statement *stmt) 257 { 258 asm_expr(stmt, EARLY); 259 } 260 261 static void match_assign_late(struct expression *expr) 262 { 263 match_assign(expr, LATE); 264 } 265 266 static void unop_expr_late(struct expression *expr) 267 { 268 unop_expr(expr, LATE); 269 } 270 271 static void asm_expr_late(struct statement *stmt) 272 { 273 asm_expr(stmt, LATE); 274 } 275 276 struct smatch_state *get_modification_state(struct expression *expr) 277 { 278 return get_state_expr(my_id, expr); 279 } 280 281 void register_modification_hooks(int id) 282 { 283 my_id = id; 284 285 set_dynamic_states(my_id); 286 287 hooks = malloc((num_checks + 1) * sizeof(*hooks)); 288 memset(hooks, 0, (num_checks + 1) * sizeof(*hooks)); 289 hooks_late = malloc((num_checks + 1) * sizeof(*hooks)); 290 memset(hooks_late, 0, (num_checks + 1) * sizeof(*hooks)); 291 292 add_hook(&match_assign_early, ASSIGNMENT_HOOK); 293 add_hook(&unop_expr_early, OP_HOOK); 294 add_hook(&asm_expr_early, ASM_HOOK); 295 } 296 297 void register_modification_hooks_late(int id) 298 { 299 add_hook(&match_call, FUNCTION_CALL_HOOK); 300 301 select_return_states_hook(PARAM_ADD, &db_param_add); 302 select_return_states_hook(PARAM_SET, &db_param_add); 303 304 add_hook(&match_assign_late, ASSIGNMENT_HOOK_AFTER); 305 add_hook(&unop_expr_late, OP_HOOK); 306 add_hook(&asm_expr_late, ASM_HOOK); 307 } 308