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         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;
 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         name = get_variable_from_key(arg, key, &sym);
 170         if (!name || !sym)
 171                 goto free;
 172 
 173         __in_fake_assign++;
 174         call_modification_hooks_name_sym(name, sym, expr, BOTH);
 175         __in_fake_assign--;
 176 
 177         other_name = get_other_name_sym(name, sym, &other_sym);
 178         if (other_name) {
 179                 __in_fake_assign++;
 180                 call_modification_hooks_name_sym(other_name, other_sym, expr, BOTH);
 181                 __in_fake_assign--;
 182                 free_string(other_name);
 183         }
 184 
 185 free:
 186         free_string(name);
 187 }
 188 
 189 static void match_assign(struct expression *expr, int late)
 190 {
 191         call_modification_hooks(expr->left, expr, late);
 192 }
 193 
 194 static void unop_expr(struct expression *expr, int late)
 195 {
 196         if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT)
 197                 return;
 198 
 199         call_modification_hooks(expr->unop, expr, late);
 200 }
 201 
 202 static void match_call(struct expression *expr)
 203 {
 204         struct expression *arg, *tmp;
 205 
 206         /* If we have the DB then trust the DB */
 207         if (!option_no_db)
 208                 return;
 209 
 210         FOR_EACH_PTR(expr->args, arg) {
 211                 tmp = strip_expr(arg);
 212                 if (tmp->type == EXPR_PREOP && tmp->op == '&')
 213                         call_modification_hooks(tmp->unop, expr, BOTH);
 214                 else
 215                         call_modification_hooks(deref_expression(tmp), expr, BOTH);
 216         } END_FOR_EACH_PTR(arg);
 217 }
 218 
 219 static void asm_expr(struct statement *stmt, int late)
 220 {
 221         struct expression *expr;
 222 
 223         FOR_EACH_PTR(stmt->asm_outputs, expr) {
 224                 if (expr->type != EXPR_ASM_OPERAND)
 225                         continue;
 226                 call_modification_hooks(expr->expr, NULL, late);
 227         } END_FOR_EACH_PTR(expr);
 228 }
 229 
 230 static void match_assign_early(struct expression *expr)
 231 {
 232         match_assign(expr, EARLY);
 233 }
 234 
 235 static void unop_expr_early(struct expression *expr)
 236 {
 237         unop_expr(expr, EARLY);
 238 }
 239 
 240 static void asm_expr_early(struct statement *stmt)
 241 {
 242         asm_expr(stmt, EARLY);
 243 }
 244 
 245 static void match_assign_late(struct expression *expr)
 246 {
 247         match_assign(expr, LATE);
 248 }
 249 
 250 static void unop_expr_late(struct expression *expr)
 251 {
 252         unop_expr(expr, LATE);
 253 }
 254 
 255 static void asm_expr_late(struct statement *stmt)
 256 {
 257         asm_expr(stmt, LATE);
 258 }
 259 
 260 struct smatch_state *get_modification_state(struct expression *expr)
 261 {
 262         return get_state_expr(my_id, expr);
 263 }
 264 
 265 void register_modification_hooks(int id)
 266 {
 267         my_id = id;
 268 
 269         set_dynamic_states(my_id);
 270 
 271         hooks = malloc((num_checks + 1) * sizeof(*hooks));
 272         memset(hooks, 0, (num_checks + 1) * sizeof(*hooks));
 273         hooks_late = malloc((num_checks + 1) * sizeof(*hooks));
 274         memset(hooks_late, 0, (num_checks + 1) * sizeof(*hooks));
 275 
 276         add_hook(&match_assign_early, ASSIGNMENT_HOOK);
 277         add_hook(&unop_expr_early, OP_HOOK);
 278         add_hook(&asm_expr_early, ASM_HOOK);
 279 }
 280 
 281 void register_modification_hooks_late(int id)
 282 {
 283         add_hook(&match_call, FUNCTION_CALL_HOOK);
 284 
 285         select_return_states_hook(PARAM_ADD, &db_param_add);
 286         select_return_states_hook(PARAM_SET, &db_param_add);
 287 
 288         add_hook(&match_assign_late, ASSIGNMENT_HOOK_AFTER);
 289         add_hook(&unop_expr_late, OP_HOOK);
 290         add_hook(&asm_expr_late, ASM_HOOK);
 291 }
 292