1 /*
   2  * Copyright (C) 2014 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  * Sometimes we aren't able to track a variable through a function call.  This
  20  * usually happens because a function changes too many variables so we give up.
  21  * Another reason this happens is because we call a function pointer and there
  22  * are too many functions which implement that function pointer so we give up.
  23  * Also maybe we don't have the database enabled.
  24  *
  25  * The goal here is to make a call back so what if we call:
  26  *
  27  *      frob(&foo);
  28  *
  29  * but we're not able to say what happens to "foo", then let's assume that we
  30  * don't know anything about "foo" if it's an untracked call.
  31  *
  32  */
  33 
  34 #include "smatch.h"
  35 #include "smatch_slist.h"
  36 #include "smatch_extra.h"
  37 
  38 static int my_id;
  39 static int tracked;
  40 
  41 STATE(untracked);
  42 
  43 typedef void (untracked_hook)(struct expression *call, int param);
  44 DECLARE_PTR_LIST(untracked_hook_list, untracked_hook *);
  45 static struct untracked_hook_list *untracked_hooks;
  46 
  47 struct int_stack *tracked_stack;
  48 
  49 void add_untracked_param_hook(void (func)(struct expression *call, int param))
  50 {
  51         untracked_hook **p = malloc(sizeof(untracked_hook *));
  52         *p = func;
  53         add_ptr_list(&untracked_hooks, p);
  54 }
  55 
  56 static void call_untracked_callbacks(struct expression *expr, int param)
  57 {
  58         untracked_hook **fn;
  59 
  60         FOR_EACH_PTR(untracked_hooks, fn) {
  61                 (*fn)(expr, param);
  62         } END_FOR_EACH_PTR(fn);
  63 }
  64 
  65 static void assume_tracked(struct expression *call_expr, int param, char *key, char *value)
  66 {
  67         tracked = 1;
  68 }
  69 
  70 void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
  71 {
  72         char *name;
  73         struct symbol *sym;
  74 
  75         while (expr->type == EXPR_ASSIGNMENT)
  76                 expr = strip_expr(expr->right);
  77         if (expr->type != EXPR_CALL)
  78                 return;
  79 
  80         name = return_state_to_var_sym(expr, param, key, &sym);
  81         if (!name || !sym)
  82                 goto free;
  83 
  84         call_untracked_callbacks(expr, param);
  85         set_state(my_id, name, sym, &untracked);
  86 free:
  87         free_string(name);
  88 }
  89 
  90 static int lost_in_va_args(struct expression *expr)
  91 {
  92         struct symbol *fn;
  93         char *name;
  94         int is_lost;
  95 
  96         fn = get_type(expr->fn);
  97         if (!fn || !fn->variadic)
  98                 return 0;
  99 
 100         is_lost = 1;
 101         name = expr_to_var(expr->fn);
 102         if (name && strstr(name, "print"))
 103                 is_lost = 0;
 104         free_string(name);
 105 
 106         return is_lost;
 107 }
 108 
 109 static void match_after_call(struct expression *expr)
 110 {
 111         struct expression *arg;
 112         struct symbol *type;
 113         int i;
 114 
 115         if (lost_in_va_args(expr))
 116                 tracked = 0;
 117 
 118         if (tracked) {
 119                 tracked = 0;
 120                 return;
 121         }
 122 
 123         i = -1;
 124         FOR_EACH_PTR(expr->args, arg) {
 125                 i++;
 126 
 127                 type = get_type(arg);
 128                 if (!type || type->type != SYM_PTR)
 129                         continue;
 130 
 131                 call_untracked_callbacks(expr, i);
 132                 set_state_expr(my_id, arg, &untracked);
 133         } END_FOR_EACH_PTR(arg);
 134 }
 135 
 136 void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
 137 {
 138         struct symbol *arg;
 139         int param;
 140 
 141         param = -1;
 142         FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
 143                 param++;
 144 
 145                 if (!arg->ident)
 146                         continue;
 147                 sql_insert_return_states(return_id, return_ranges,
 148                                          UNTRACKED_PARAM, param, "$", "");
 149         } END_FOR_EACH_PTR(arg);
 150 }
 151 
 152 static void print_untracked_params(int return_id, char *return_ranges, struct expression *expr)
 153 {
 154         struct symbol *arg;
 155         int param;
 156 
 157         param = -1;
 158         FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
 159                 param++;
 160 
 161                 if (!arg->ident)
 162                         continue;
 163                 if (!get_state(my_id, arg->ident->name, arg) &&
 164                     !__bail_on_rest_of_function)  /* hairy functions are untrackable */
 165                         continue;
 166 
 167                 sql_insert_return_states(return_id, return_ranges,
 168                                          UNTRACKED_PARAM, param, "$", "");
 169         } END_FOR_EACH_PTR(arg);
 170 }
 171 
 172 static void match_param_assign(struct expression *expr)
 173 {
 174         struct expression *right;
 175         struct symbol *type;
 176         int param;
 177 
 178         if (__in_fake_assign)
 179                 return;
 180 
 181         right = strip_expr(expr->right);
 182         type = get_type(right);
 183         if (!type || type->type != SYM_PTR)
 184                 return;
 185 
 186         param = get_param_num(right);
 187         if (param < 0)
 188                 return;
 189 
 190         set_state_expr(my_id, right, &untracked);
 191 }
 192 
 193 
 194 static void match_param_assign_in_asm(struct statement *stmt)
 195 {
 196 
 197         struct expression *expr;
 198         struct symbol *type;
 199         int state = 0;
 200         int param;
 201 
 202         FOR_EACH_PTR(stmt->asm_inputs, expr) {
 203                 switch (state) {
 204                 case 0: /* identifier */
 205                 case 1: /* constraint */
 206                         state++;
 207                         continue;
 208                 case 2: /* expression */
 209                         state = 0;
 210 
 211                         expr = strip_expr(expr);
 212                         type = get_type(expr);
 213                         if (!type || type->type != SYM_PTR)
 214                                 continue;
 215                         param = get_param_num(expr);
 216                         if (param < 0)
 217                                 continue;
 218                         set_state_expr(my_id, expr, &untracked);
 219                         continue;
 220                 }
 221         } END_FOR_EACH_PTR(expr);
 222 }
 223 
 224 static void match_inline_start(struct expression *expr)
 225 {
 226         push_int(&tracked_stack, tracked);
 227 }
 228 
 229 static void match_inline_end(struct expression *expr)
 230 {
 231         tracked = pop_int(&tracked_stack);
 232 }
 233 
 234 void register_untracked_param(int id)
 235 {
 236         my_id = id;
 237 
 238         select_return_states_hook(INTERNAL, &assume_tracked);
 239         select_return_states_hook(UNTRACKED_PARAM, &mark_untracked);
 240         add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB);
 241 
 242         add_split_return_callback(&print_untracked_params);
 243 
 244         add_hook(&match_param_assign, ASSIGNMENT_HOOK);
 245         add_hook(&match_param_assign_in_asm, ASM_HOOK);
 246 
 247         add_hook(&match_inline_start, INLINE_FN_START);
 248         add_hook(&match_inline_end, INLINE_FN_END);
 249 }