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 *cur, struct sm_state *other)
  28 {
  29         if (is_impossible_path())
  30                 set_state(my_id, cur->name, cur->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 (!expr_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(&param_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 }