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