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 STATE(lost);
  43 
  44 typedef void (untracked_hook)(struct expression *call, int param);
  45 DECLARE_PTR_LIST(untracked_hook_list, untracked_hook *);
  46 static struct untracked_hook_list *untracked_hooks;
  47 static struct untracked_hook_list *lost_hooks;
  48 
  49 struct int_stack *tracked_stack;
  50 
  51 void add_untracked_param_hook(void (func)(struct expression *call, int param))
  52 {
  53         untracked_hook **p = malloc(sizeof(untracked_hook *));
  54         *p = func;
  55         add_ptr_list(&untracked_hooks, p);
  56 }
  57 
  58 static void call_untracked_callbacks(struct expression *expr, int param)
  59 {
  60         untracked_hook **fn;
  61 
  62         FOR_EACH_PTR(untracked_hooks, fn) {
  63                 (*fn)(expr, param);
  64         } END_FOR_EACH_PTR(fn);
  65 }
  66 
  67 void add_lost_param_hook(void (func)(struct expression *call, int param))
  68 {
  69         untracked_hook **p = malloc(sizeof(untracked_hook *));
  70         *p = func;
  71         add_ptr_list(&lost_hooks, p);
  72 }
  73 
  74 static void call_lost_callbacks(struct expression *expr, int param)
  75 {
  76         untracked_hook **fn;
  77 
  78         FOR_EACH_PTR(lost_hooks, fn) {
  79                 (*fn)(expr, param);
  80         } END_FOR_EACH_PTR(fn);
  81 }
  82 
  83 static void assume_tracked(struct expression *call_expr, int param, char *key, char *value)
  84 {
  85         tracked = 1;
  86 }
  87 
  88 static char *get_array_from_key(struct expression *expr, int param, const char *key, struct symbol **sym)
  89 {
  90         struct expression *arg;
  91 
  92         arg = get_argument_from_call_expr(expr->args, param);
  93         if (!arg)
  94                 return NULL;
  95         if (arg->type != EXPR_PREOP || arg->op != '&')
  96                 return NULL;
  97         arg = arg->unop;
  98         if (!is_array(arg))
  99                 return NULL;
 100         arg = get_array_base(arg);
 101 
 102         return expr_to_var_sym(arg, sym);
 103 }
 104 
 105 static void mark_untracked_lost(struct expression *expr, int param, const char *key, int type)
 106 {
 107         char *name;
 108         struct symbol *sym;
 109 
 110         while (expr->type == EXPR_ASSIGNMENT)
 111                 expr = strip_expr(expr->right);
 112         if (expr->type != EXPR_CALL)
 113                 return;
 114 
 115         name = return_state_to_var_sym(expr, param, key, &sym);
 116         if (!name || !sym) {
 117                 name = get_array_from_key(expr, param, key, &sym);
 118                 if (!name || !sym)
 119                         goto free;
 120         }
 121 
 122         if (type == LOST_PARAM)
 123                 call_lost_callbacks(expr, param);
 124         call_untracked_callbacks(expr, param);
 125         set_state(my_id, name, sym, &untracked);
 126 free:
 127         free_string(name);
 128 
 129 }
 130 
 131 void mark_untracked(struct expression *expr, int param, const char *key, const char *value)
 132 {
 133         mark_untracked_lost(expr, param, key, UNTRACKED_PARAM);
 134 }
 135 
 136 void mark_lost(struct expression *expr, int param, const char *key, const char *value)
 137 {
 138         mark_untracked_lost(expr, param, key, LOST_PARAM);
 139 }
 140 
 141 static int lost_in_va_args(struct expression *expr)
 142 {
 143         struct symbol *fn;
 144         char *name;
 145         int is_lost;
 146 
 147         fn = get_type(expr->fn);
 148         if (!fn || !fn->variadic)
 149                 return 0;
 150 
 151         is_lost = 1;
 152         name = expr_to_var(expr->fn);
 153         if (name && strstr(name, "print"))
 154                 is_lost = 0;
 155         free_string(name);
 156 
 157         return is_lost;
 158 }
 159 
 160 static void match_after_call(struct expression *expr)
 161 {
 162         struct expression *arg;
 163         struct symbol *type;
 164         int i;
 165 
 166         if (lost_in_va_args(expr))
 167                 tracked = 0;
 168 
 169         if (tracked) {
 170                 tracked = 0;
 171                 return;
 172         }
 173 
 174         i = -1;
 175         FOR_EACH_PTR(expr->args, arg) {
 176                 i++;
 177 
 178                 type = get_type(arg);
 179                 if (!type || type->type != SYM_PTR)
 180                         continue;
 181 
 182                 call_untracked_callbacks(expr, i);
 183                 set_state_expr(my_id, arg, &untracked);
 184         } END_FOR_EACH_PTR(arg);
 185 }
 186 
 187 
 188 static void mark_all_params(int return_id, char *return_ranges, int type)
 189 {
 190         struct symbol *arg;
 191         int param;
 192 
 193         param = -1;
 194         FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
 195                 param++;
 196 
 197                 if (!arg->ident)
 198                         continue;
 199                 sql_insert_return_states(return_id, return_ranges,
 200                                          type, param, "$", "");
 201         } END_FOR_EACH_PTR(arg);
 202 }
 203 
 204 
 205 void mark_all_params_untracked(int return_id, char *return_ranges, struct expression *expr)
 206 {
 207         mark_all_params(return_id, return_ranges, UNTRACKED_PARAM);
 208 }
 209 
 210 void mark_all_params_lost(int return_id, char *return_ranges, struct expression *expr)
 211 {
 212         mark_all_params(return_id, return_ranges, LOST_PARAM);
 213 }
 214 
 215 static void print_untracked_params(int return_id, char *return_ranges, struct expression *expr)
 216 {
 217         struct sm_state *sm;
 218         struct symbol *arg;
 219         int param;
 220         int type;
 221 
 222         param = -1;
 223         FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
 224                 param++;
 225 
 226                 if (!arg->ident)
 227                         continue;
 228 
 229                 if (__bail_on_rest_of_function) {
 230                         /* hairy functions are lost */
 231                         type = LOST_PARAM;
 232                 } else if ((sm = get_sm_state(my_id, arg->ident->name, arg))) {
 233                         if (slist_has_state(sm->possible, &lost))
 234                                 type = LOST_PARAM;
 235                         else
 236                                 type = UNTRACKED_PARAM;
 237                 } else {
 238                         continue;
 239                 }
 240 
 241                 sql_insert_return_states(return_id, return_ranges,
 242                                          type, param, "$", "");
 243         } END_FOR_EACH_PTR(arg);
 244 }
 245 
 246 static void match_param_assign(struct expression *expr)
 247 {
 248         struct expression *right;
 249         struct symbol *type;
 250         int param;
 251 
 252         if (__in_fake_assign)
 253                 return;
 254 
 255         right = strip_expr(expr->right);
 256         type = get_type(right);
 257         if (!type || type->type != SYM_PTR)
 258                 return;
 259 
 260         param = get_param_num(right);
 261         if (param < 0)
 262                 return;
 263 
 264         set_state_expr(my_id, right, &untracked);
 265 }
 266 
 267 
 268 static void match_param_assign_in_asm(struct statement *stmt)
 269 {
 270 
 271         struct expression *tmp, *expr;
 272         struct symbol *type;
 273         int param;
 274 
 275         FOR_EACH_PTR(stmt->asm_inputs, tmp) {
 276                 expr = strip_expr(tmp->expr);
 277                 type = get_type(expr);
 278                 if (!type || type->type != SYM_PTR)
 279                         continue;
 280                 param = get_param_num(expr);
 281                 if (param < 0)
 282                         continue;
 283                 set_state_expr(my_id, expr, &untracked);
 284         } END_FOR_EACH_PTR(tmp);
 285 }
 286 
 287 static void match_inline_start(struct expression *expr)
 288 {
 289         push_int(&tracked_stack, tracked);
 290 }
 291 
 292 static void match_inline_end(struct expression *expr)
 293 {
 294         tracked = pop_int(&tracked_stack);
 295 }
 296 
 297 void register_untracked_param(int id)
 298 {
 299         my_id = id;
 300 
 301         select_return_states_hook(INTERNAL, &assume_tracked);
 302         select_return_states_hook(UNTRACKED_PARAM, &mark_untracked);
 303         select_return_states_hook(LOST_PARAM, &mark_lost);
 304         add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB);
 305 
 306         add_split_return_callback(&print_untracked_params);
 307 
 308         add_hook(&match_param_assign, ASSIGNMENT_HOOK);
 309         add_hook(&match_param_assign_in_asm, ASM_HOOK);
 310 
 311         add_hook(&match_inline_start, INLINE_FN_START);
 312         add_hook(&match_inline_end, INLINE_FN_END);
 313 }