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