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 *expr; 272 struct symbol *type; 273 int state = 0; 274 int param; 275 276 FOR_EACH_PTR(stmt->asm_inputs, expr) { 277 switch (state) { 278 case 0: /* identifier */ 279 case 1: /* constraint */ 280 state++; 281 continue; 282 case 2: /* expression */ 283 state = 0; 284 285 expr = strip_expr(expr); 286 type = get_type(expr); 287 if (!type || type->type != SYM_PTR) 288 continue; 289 param = get_param_num(expr); 290 if (param < 0) 291 continue; 292 set_state_expr(my_id, expr, &untracked); 293 continue; 294 } 295 } END_FOR_EACH_PTR(expr); 296 } 297 298 static void match_inline_start(struct expression *expr) 299 { 300 push_int(&tracked_stack, tracked); 301 } 302 303 static void match_inline_end(struct expression *expr) 304 { 305 tracked = pop_int(&tracked_stack); 306 } 307 308 void register_untracked_param(int id) 309 { 310 my_id = id; 311 312 select_return_states_hook(INTERNAL, &assume_tracked); 313 select_return_states_hook(UNTRACKED_PARAM, &mark_untracked); 314 select_return_states_hook(LOST_PARAM, &mark_lost); 315 add_hook(&match_after_call, FUNCTION_CALL_HOOK_AFTER_DB); 316 317 add_split_return_callback(&print_untracked_params); 318 319 add_hook(&match_param_assign, ASSIGNMENT_HOOK); 320 add_hook(&match_param_assign_in_asm, ASM_HOOK); 321 322 add_hook(&match_inline_start, INLINE_FN_START); 323 add_hook(&match_inline_end, INLINE_FN_END); 324 } 325