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 #include "smatch.h" 19 #include "smatch_slist.h" 20 #include "smatch_extra.h" 21 22 static int my_id; 23 24 STATE(uninitialized); 25 STATE(initialized); 26 27 static void pre_merge_hook(struct sm_state *sm) 28 { 29 if (is_impossible_path()) 30 set_state(my_id, sm->name, sm->sym, &initialized); 31 } 32 33 static void mark_members_uninitialized(struct symbol *sym) 34 { 35 struct symbol *struct_type, *tmp, *base_type; 36 char buf[256]; 37 38 struct_type = get_real_base_type(sym); 39 FOR_EACH_PTR(struct_type->symbol_list, tmp) { 40 if (!tmp->ident) 41 continue; 42 base_type = get_real_base_type(tmp); 43 if (!base_type || 44 base_type->type == SYM_STRUCT || 45 base_type->type == SYM_ARRAY || 46 base_type->type == SYM_UNION) 47 continue; 48 snprintf(buf, sizeof(buf), "%s.%s", sym->ident->name, tmp->ident->name); 49 set_state(my_id, buf, sym, &uninitialized); 50 } END_FOR_EACH_PTR(tmp); 51 } 52 53 static void match_declarations(struct symbol *sym) 54 { 55 struct symbol *type; 56 57 if (sym->initializer) 58 return; 59 60 type = get_real_base_type(sym); 61 /* Smatch is crap at tracking arrays */ 62 if (type->type == SYM_ARRAY) 63 return; 64 if (type->type == SYM_UNION) 65 return; 66 if (sym->ctype.modifiers & MOD_STATIC) 67 return; 68 69 if (!sym->ident) 70 return; 71 72 if (type->type == SYM_STRUCT) { 73 mark_members_uninitialized(sym); 74 return; 75 } 76 77 set_state(my_id, sym->ident->name, sym, &uninitialized); 78 } 79 80 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) 81 { 82 if (!sym || !sym->ident) 83 return; 84 if (strcmp(name, sym->ident->name) != 0) 85 return; 86 set_state(my_id, name, sym, &initialized); 87 } 88 89 static void match_assign(struct expression *expr) 90 { 91 struct expression *right; 92 93 right = strip_expr(expr->right); 94 if (right->type == EXPR_PREOP && right->op == '&') 95 set_state_expr(my_id, right->unop, &initialized); 96 } 97 98 static void match_negative_comparison(struct expression *expr) 99 { 100 struct expression *success; 101 struct sm_state *sm; 102 sval_t max; 103 104 /* 105 * In the kernel, people don't use "if (ret) {" and "if (ret < 0) {" 106 * consistently. Ideally Smatch would know the return but often it 107 * doesn't. 108 * 109 */ 110 111 if (option_project != PROJ_KERNEL) 112 return; 113 114 if (expr->type != EXPR_COMPARE || expr->op != '<') 115 return; 116 if (!is_zero(expr->right)) 117 return; 118 if (get_implied_max(expr->left, &max) && max.value == 0) 119 return; 120 121 success = compare_expression(expr->left, SPECIAL_EQUAL, expr->right); 122 if (!assume(success)) 123 return; 124 125 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 126 if (sm->state == &initialized) 127 set_true_false_states(my_id, sm->name, sm->sym, NULL, &initialized); 128 } END_FOR_EACH_SM(sm); 129 130 end_assume(); 131 } 132 133 static int is_initialized(struct expression *expr) 134 { 135 struct sm_state *sm; 136 137 expr = strip_expr(expr); 138 if (expr->type != EXPR_SYMBOL) 139 return 1; 140 sm = get_sm_state_expr(my_id, expr); 141 if (!sm) 142 return 1; 143 if (!slist_has_state(sm->possible, &uninitialized)) 144 return 1; 145 return 0; 146 } 147 148 static void match_dereferences(struct expression *expr) 149 { 150 char *name; 151 152 if (implications_off || parse_error) 153 return; 154 155 if (expr->type != EXPR_PREOP) 156 return; 157 if (is_impossible_path()) 158 return; 159 if (is_initialized(expr->unop)) 160 return; 161 162 name = expr_to_str(expr->unop); 163 sm_error("potentially dereferencing uninitialized '%s'.", name); 164 free_string(name); 165 166 set_state_expr(my_id, expr->unop, &initialized); 167 } 168 169 static void match_condition(struct expression *expr) 170 { 171 char *name; 172 173 if (implications_off || parse_error) 174 return; 175 176 if (is_impossible_path()) 177 return; 178 179 if (is_initialized(expr)) 180 return; 181 182 name = expr_to_str(expr); 183 sm_error("potentially using uninitialized '%s'.", name); 184 free_string(name); 185 186 set_state_expr(my_id, expr, &initialized); 187 } 188 189 static void match_call(struct expression *expr) 190 { 191 struct expression *arg; 192 char *name; 193 194 if (parse_error) 195 return; 196 197 if (is_impossible_path()) 198 return; 199 200 FOR_EACH_PTR(expr->args, arg) { 201 if (is_initialized(arg)) 202 continue; 203 204 name = expr_to_str(arg); 205 sm_warning("passing uninitialized '%s'", name); 206 free_string(name); 207 208 set_state_expr(my_id, arg, &initialized); 209 } END_FOR_EACH_PTR(arg); 210 } 211 212 static int param_used_callback(void *found, int argc, char **argv, char **azColName) 213 { 214 *(int *)found = 1; 215 return 0; 216 } 217 218 static int member_is_used(struct expression *call, int param, char *printed_name) 219 { 220 int found; 221 222 /* for function pointers assume everything is used */ 223 if (call->fn->type != EXPR_SYMBOL) 224 return 0; 225 226 found = 0; 227 run_sql(¶m_used_callback, &found, 228 "select * from return_implies where %s and type = %d and parameter = %d and key = '%s';", 229 get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name); 230 return found; 231 } 232 233 static void match_call_struct_members(struct expression *expr) 234 { 235 struct symbol *type, *sym; 236 struct expression *arg; 237 struct sm_state *sm; 238 char *arg_name; 239 char buf[256]; 240 int param; 241 242 return; 243 244 if (parse_error) 245 return; 246 247 param = -1; 248 FOR_EACH_PTR(expr->args, arg) { 249 param++; 250 if (arg->type != EXPR_PREOP || arg->op != '&') 251 continue; 252 type = get_type(arg->unop); 253 if (!type || type->type != SYM_STRUCT) 254 continue; 255 arg_name = expr_to_var_sym(arg->unop, &sym); 256 if (!arg_name || !sym) 257 goto free; 258 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 259 if (sm->sym != sym) 260 continue; 261 if (!slist_has_state(sm->possible, &uninitialized)) 262 continue; 263 snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1); 264 if (!member_is_used(expr, param, buf)) 265 goto free; 266 sm_warning("struct member %s is uninitialized", sm->name); 267 } END_FOR_EACH_SM(sm); 268 269 free: 270 free_string(arg_name); 271 } END_FOR_EACH_PTR(arg); 272 } 273 274 static int is_being_modified(struct expression *expr) 275 { 276 struct expression *parent; 277 struct statement *stmt; 278 279 parent = expr_get_parent_expr(expr); 280 if (!parent) 281 return 0; 282 while (parent->type == EXPR_PREOP && parent->op == '(') { 283 parent = expr_get_parent_expr(parent); 284 if (!parent) 285 return 0; 286 } 287 if (parent->type == EXPR_PREOP && parent->op == '&') 288 return 1; 289 if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr)) 290 return 1; 291 292 stmt = last_ptr_list((struct ptr_list *)big_statement_stack); 293 if (stmt && stmt->type == STMT_ASM) 294 return 1; 295 296 return 0; 297 } 298 299 static void match_symbol(struct expression *expr) 300 { 301 char *name; 302 303 if (implications_off || parse_error) 304 return; 305 306 if (is_impossible_path()) 307 return; 308 309 if (is_initialized(expr)) 310 return; 311 312 if (is_being_modified(expr)) 313 return; 314 315 name = expr_to_str(expr); 316 sm_error("uninitialized symbol '%s'.", name); 317 free_string(name); 318 319 set_state_expr(my_id, expr, &initialized); 320 } 321 322 static void match_untracked(struct expression *call, int param) 323 { 324 struct expression *arg; 325 326 arg = get_argument_from_call_expr(call->args, param); 327 arg = strip_expr(arg); 328 if (!arg || arg->type != EXPR_PREOP || arg->op != '&') 329 return; 330 arg = strip_expr(arg->unop); 331 set_state_expr(my_id, arg, &initialized); 332 } 333 334 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr) 335 { 336 int arg_nr = PTR_INT(_arg_nr); 337 struct expression *arg; 338 339 arg = get_argument_from_call_expr(expr->args, arg_nr); 340 arg = strip_expr(arg); 341 if (!arg) 342 return; 343 if (arg->type != EXPR_PREOP || arg->op != '&') 344 return; 345 arg = strip_expr(arg->unop); 346 set_state_expr(my_id, arg, &initialized); 347 } 348 349 static void register_ignored_params_from_file(void) 350 { 351 char name[256]; 352 struct token *token; 353 const char *func; 354 char prev_func[256]; 355 int param; 356 357 memset(prev_func, 0, sizeof(prev_func)); 358 snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str); 359 name[255] = '\0'; 360 token = get_tokens_file(name); 361 if (!token) 362 return; 363 if (token_type(token) != TOKEN_STREAMBEGIN) 364 return; 365 token = token->next; 366 while (token_type(token) != TOKEN_STREAMEND) { 367 if (token_type(token) != TOKEN_IDENT) 368 return; 369 func = show_ident(token->ident); 370 371 token = token->next; 372 if (token_type(token) != TOKEN_NUMBER) 373 return; 374 param = atoi(token->number); 375 376 add_function_hook(func, &match_ignore_param, INT_PTR(param)); 377 378 token = token->next; 379 } 380 clear_token_alloc(); 381 } 382 383 void check_uninitialized(int id) 384 { 385 my_id = id; 386 387 add_hook(&match_declarations, DECLARATION_HOOK); 388 add_extra_mod_hook(&extra_mod_hook); 389 add_hook(&match_assign, ASSIGNMENT_HOOK); 390 add_hook(&match_negative_comparison, CONDITION_HOOK); 391 add_untracked_param_hook(&match_untracked); 392 add_pre_merge_hook(my_id, &pre_merge_hook); 393 394 add_hook(&match_dereferences, DEREF_HOOK); 395 add_hook(&match_condition, CONDITION_HOOK); 396 add_hook(&match_call, FUNCTION_CALL_HOOK); 397 add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK); 398 add_hook(&match_symbol, SYM_HOOK); 399 400 register_ignored_params_from_file(); 401 } 402