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 int is_initialized(struct expression *expr) 99 { 100 struct sm_state *sm; 101 102 expr = strip_expr(expr); 103 if (expr->type != EXPR_SYMBOL) 104 return 1; 105 sm = get_sm_state_expr(my_id, expr); 106 if (!sm) 107 return 1; 108 if (!slist_has_state(sm->possible, &uninitialized)) 109 return 1; 110 return 0; 111 } 112 113 static void match_dereferences(struct expression *expr) 114 { 115 char *name; 116 117 if (parse_error) 118 return; 119 120 if (expr->type != EXPR_PREOP) 121 return; 122 if (is_impossible_path()) 123 return; 124 if (is_initialized(expr->unop)) 125 return; 126 127 name = expr_to_str(expr->unop); 128 sm_error("potentially dereferencing uninitialized '%s'.", name); 129 free_string(name); 130 131 set_state_expr(my_id, expr->unop, &initialized); 132 } 133 134 static void match_condition(struct expression *expr) 135 { 136 char *name; 137 138 if (parse_error) 139 return; 140 141 if (is_impossible_path()) 142 return; 143 144 if (is_initialized(expr)) 145 return; 146 147 name = expr_to_str(expr); 148 sm_error("potentially using uninitialized '%s'.", name); 149 free_string(name); 150 151 set_state_expr(my_id, expr, &initialized); 152 } 153 154 static void match_call(struct expression *expr) 155 { 156 struct expression *arg; 157 char *name; 158 159 if (parse_error) 160 return; 161 162 if (is_impossible_path()) 163 return; 164 165 FOR_EACH_PTR(expr->args, arg) { 166 if (is_initialized(arg)) 167 continue; 168 169 name = expr_to_str(arg); 170 sm_warning("passing uninitialized '%s'", name); 171 free_string(name); 172 173 set_state_expr(my_id, arg, &initialized); 174 } END_FOR_EACH_PTR(arg); 175 } 176 177 static int param_used_callback(void *found, int argc, char **argv, char **azColName) 178 { 179 *(int *)found = 1; 180 return 0; 181 } 182 183 static int member_is_used(struct expression *call, int param, char *printed_name) 184 { 185 int found; 186 187 /* for function pointers assume everything is used */ 188 if (call->fn->type != EXPR_SYMBOL) 189 return 0; 190 191 found = 0; 192 run_sql(¶m_used_callback, &found, 193 "select * from return_implies where %s and type = %d and parameter = %d and key = '%s';", 194 get_static_filter(call->fn->symbol), PARAM_USED, param, printed_name); 195 return found; 196 } 197 198 static void match_call_struct_members(struct expression *expr) 199 { 200 struct symbol *type, *sym; 201 struct expression *arg; 202 struct sm_state *sm; 203 char *arg_name; 204 char buf[256]; 205 int param; 206 207 return; 208 209 if (parse_error) 210 return; 211 212 param = -1; 213 FOR_EACH_PTR(expr->args, arg) { 214 param++; 215 if (arg->type != EXPR_PREOP || arg->op != '&') 216 continue; 217 type = get_type(arg->unop); 218 if (!type || type->type != SYM_STRUCT) 219 continue; 220 arg_name = expr_to_var_sym(arg->unop, &sym); 221 if (!arg_name || !sym) 222 goto free; 223 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 224 if (sm->sym != sym) 225 continue; 226 if (!slist_has_state(sm->possible, &uninitialized)) 227 continue; 228 snprintf(buf, sizeof(buf), "$->%s", sm->name + strlen(arg_name) + 1); 229 if (!member_is_used(expr, param, buf)) 230 goto free; 231 sm_warning("struct member %s is uninitialized", sm->name); 232 } END_FOR_EACH_SM(sm); 233 234 free: 235 free_string(arg_name); 236 } END_FOR_EACH_PTR(arg); 237 } 238 239 static int is_being_modified(struct expression *expr) 240 { 241 struct expression *parent; 242 struct statement *stmt; 243 244 parent = expr_get_parent_expr(expr); 245 if (!parent) 246 return 0; 247 while (parent->type == EXPR_PREOP && parent->op == '(') { 248 parent = expr_get_parent_expr(parent); 249 if (!parent) 250 return 0; 251 } 252 if (parent->type == EXPR_PREOP && parent->op == '&') 253 return 1; 254 if (parent->type == EXPR_ASSIGNMENT && expr_equiv(parent->left, expr)) 255 return 1; 256 257 stmt = last_ptr_list((struct ptr_list *)big_statement_stack); 258 if (stmt && stmt->type == STMT_ASM) 259 return 1; 260 261 return 0; 262 } 263 264 static void match_symbol(struct expression *expr) 265 { 266 char *name; 267 268 if (parse_error) 269 return; 270 271 if (is_impossible_path()) 272 return; 273 274 if (is_initialized(expr)) 275 return; 276 277 if (is_being_modified(expr)) 278 return; 279 280 name = expr_to_str(expr); 281 sm_error("uninitialized symbol '%s'.", name); 282 free_string(name); 283 284 set_state_expr(my_id, expr, &initialized); 285 } 286 287 static void match_untracked(struct expression *call, int param) 288 { 289 struct expression *arg; 290 291 arg = get_argument_from_call_expr(call->args, param); 292 arg = strip_expr(arg); 293 if (!arg || arg->type != EXPR_PREOP || arg->op != '&') 294 return; 295 arg = strip_expr(arg->unop); 296 set_state_expr(my_id, arg, &initialized); 297 } 298 299 static void match_ignore_param(const char *fn, struct expression *expr, void *_arg_nr) 300 { 301 int arg_nr = PTR_INT(_arg_nr); 302 struct expression *arg; 303 304 arg = get_argument_from_call_expr(expr->args, arg_nr); 305 arg = strip_expr(arg); 306 if (!arg) 307 return; 308 if (arg->type != EXPR_PREOP || arg->op != '&') 309 return; 310 arg = strip_expr(arg->unop); 311 set_state_expr(my_id, arg, &initialized); 312 } 313 314 static void register_ignored_params_from_file(void) 315 { 316 char name[256]; 317 struct token *token; 318 const char *func; 319 char prev_func[256]; 320 int param; 321 322 memset(prev_func, 0, sizeof(prev_func)); 323 snprintf(name, 256, "%s.ignore_uninitialized_param", option_project_str); 324 name[255] = '\0'; 325 token = get_tokens_file(name); 326 if (!token) 327 return; 328 if (token_type(token) != TOKEN_STREAMBEGIN) 329 return; 330 token = token->next; 331 while (token_type(token) != TOKEN_STREAMEND) { 332 if (token_type(token) != TOKEN_IDENT) 333 return; 334 func = show_ident(token->ident); 335 336 token = token->next; 337 if (token_type(token) != TOKEN_NUMBER) 338 return; 339 param = atoi(token->number); 340 341 add_function_hook(func, &match_ignore_param, INT_PTR(param)); 342 343 token = token->next; 344 } 345 clear_token_alloc(); 346 } 347 348 void check_uninitialized(int id) 349 { 350 my_id = id; 351 352 add_hook(&match_declarations, DECLARATION_HOOK); 353 add_extra_mod_hook(&extra_mod_hook); 354 add_hook(&match_assign, ASSIGNMENT_HOOK); 355 add_untracked_param_hook(&match_untracked); 356 add_pre_merge_hook(my_id, &pre_merge_hook); 357 358 add_hook(&match_dereferences, DEREF_HOOK); 359 add_hook(&match_condition, CONDITION_HOOK); 360 add_hook(&match_call, FUNCTION_CALL_HOOK); 361 add_hook(&match_call_struct_members, FUNCTION_CALL_HOOK); 362 add_hook(&match_symbol, SYM_HOOK); 363 364 register_ignored_params_from_file(); 365 } 366